V4L/DVB (12379): uvcvideo: Multiple streaming interfaces support
authorLaurent Pinchart <laurent.pinchart@skynet.be>
Wed, 1 Jul 2009 23:24:47 +0000 (20:24 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Sat, 12 Sep 2009 15:18:33 +0000 (12:18 -0300)
Restructure the UVC descriptors parsing code to handle multiple streaming
interfaces. The driver now creates a uvc_video_chain instance for each chain
detected in the UVC control interface descriptors, and tries to register one
video device per streaming endpoint.

Signed-off-by: Laurent Pinchart <laurent.pinchart@skynet.be>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/uvc/uvc_ctrl.c
drivers/media/video/uvc/uvc_driver.c
drivers/media/video/uvc/uvc_v4l2.c
drivers/media/video/uvc/uvc_video.c
drivers/media/video/uvc/uvcvideo.h

index 70043b1..c3225a5 100644 (file)
@@ -729,7 +729,7 @@ static void __uvc_find_control(struct uvc_entity *entity, __u32 v4l2_id,
        }
 }
 
-struct uvc_control *uvc_find_control(struct uvc_video_device *video,
+struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
        __u32 v4l2_id, struct uvc_control_mapping **mapping)
 {
        struct uvc_control *ctrl = NULL;
@@ -742,17 +742,17 @@ struct uvc_control *uvc_find_control(struct uvc_video_device *video,
        v4l2_id &= V4L2_CTRL_ID_MASK;
 
        /* Find the control. */
-       __uvc_find_control(video->processing, v4l2_id, mapping, &ctrl, next);
+       __uvc_find_control(chain->processing, v4l2_id, mapping, &ctrl, next);
        if (ctrl && !next)
                return ctrl;
 
-       list_for_each_entry(entity, &video->iterms, chain) {
+       list_for_each_entry(entity, &chain->iterms, chain) {
                __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next);
                if (ctrl && !next)
                        return ctrl;
        }
 
-       list_for_each_entry(entity, &video->extensions, chain) {
+       list_for_each_entry(entity, &chain->extensions, chain) {
                __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next);
                if (ctrl && !next)
                        return ctrl;
@@ -765,7 +765,7 @@ struct uvc_control *uvc_find_control(struct uvc_video_device *video,
        return ctrl;
 }
 
-int uvc_query_v4l2_ctrl(struct uvc_video_device *video,
+int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
        struct v4l2_queryctrl *v4l2_ctrl)
 {
        struct uvc_control *ctrl;
@@ -775,7 +775,7 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video,
        __u8 *data;
        int ret;
 
-       ctrl = uvc_find_control(video, v4l2_ctrl->id, &mapping);
+       ctrl = uvc_find_control(chain, v4l2_ctrl->id, &mapping);
        if (ctrl == NULL)
                return -EINVAL;
 
@@ -793,9 +793,9 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video,
                v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
 
        if (ctrl->info->flags & UVC_CONTROL_GET_DEF) {
-               ret = uvc_query_ctrl(video->dev, UVC_GET_DEF, ctrl->entity->id,
-                               video->dev->intfnum, ctrl->info->selector, data,
-                               ctrl->info->size);
+               ret = uvc_query_ctrl(chain->dev, UVC_GET_DEF, ctrl->entity->id,
+                                    chain->dev->intfnum, ctrl->info->selector,
+                                    data, ctrl->info->size);
                if (ret < 0)
                        goto out;
                v4l2_ctrl->default_value =
@@ -831,25 +831,25 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video,
        }
 
        if (ctrl->info->flags & UVC_CONTROL_GET_MIN) {
-               ret = uvc_query_ctrl(video->dev, UVC_GET_MIN, ctrl->entity->id,
-                               video->dev->intfnum, ctrl->info->selector, data,
-                               ctrl->info->size);
+               ret = uvc_query_ctrl(chain->dev, UVC_GET_MIN, ctrl->entity->id,
+                                    chain->dev->intfnum, ctrl->info->selector,
+                                    data, ctrl->info->size);
                if (ret < 0)
                        goto out;
                v4l2_ctrl->minimum = mapping->get(mapping, UVC_GET_MIN, data);
        }
        if (ctrl->info->flags & UVC_CONTROL_GET_MAX) {
-               ret = uvc_query_ctrl(video->dev, UVC_GET_MAX, ctrl->entity->id,
-                               video->dev->intfnum, ctrl->info->selector, data,
-                               ctrl->info->size);
+               ret = uvc_query_ctrl(chain->dev, UVC_GET_MAX, ctrl->entity->id,
+                                    chain->dev->intfnum, ctrl->info->selector,
+                                    data, ctrl->info->size);
                if (ret < 0)
                        goto out;
                v4l2_ctrl->maximum = mapping->get(mapping, UVC_GET_MAX, data);
        }
        if (ctrl->info->flags & UVC_CONTROL_GET_RES) {
-               ret = uvc_query_ctrl(video->dev, UVC_GET_RES, ctrl->entity->id,
-                               video->dev->intfnum, ctrl->info->selector, data,
-                               ctrl->info->size);
+               ret = uvc_query_ctrl(chain->dev, UVC_GET_RES, ctrl->entity->id,
+                                    chain->dev->intfnum, ctrl->info->selector,
+                                    data, ctrl->info->size);
                if (ret < 0)
                        goto out;
                v4l2_ctrl->step = mapping->get(mapping, UVC_GET_RES, data);
@@ -886,9 +886,9 @@ out:
  * (UVC_CTRL_DATA_BACKUP) for all dirty controls. Both functions release the
  * control lock.
  */
-int uvc_ctrl_begin(struct uvc_video_device *video)
+int uvc_ctrl_begin(struct uvc_video_chain *chain)
 {
-       return mutex_lock_interruptible(&video->ctrl_mutex) ? -ERESTARTSYS : 0;
+       return mutex_lock_interruptible(&chain->ctrl_mutex) ? -ERESTARTSYS : 0;
 }
 
 static int uvc_ctrl_commit_entity(struct uvc_device *dev,
@@ -938,34 +938,34 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev,
        return 0;
 }
 
-int __uvc_ctrl_commit(struct uvc_video_device *video, int rollback)
+int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback)
 {
        struct uvc_entity *entity;
        int ret = 0;
 
        /* Find the control. */
-       ret = uvc_ctrl_commit_entity(video->dev, video->processing, rollback);
+       ret = uvc_ctrl_commit_entity(chain->dev, chain->processing, rollback);
        if (ret < 0)
                goto done;
 
-       list_for_each_entry(entity, &video->iterms, chain) {
-               ret = uvc_ctrl_commit_entity(video->dev, entity, rollback);
+       list_for_each_entry(entity, &chain->iterms, chain) {
+               ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback);
                if (ret < 0)
                        goto done;
        }
 
-       list_for_each_entry(entity, &video->extensions, chain) {
-               ret = uvc_ctrl_commit_entity(video->dev, entity, rollback);
+       list_for_each_entry(entity, &chain->extensions, chain) {
+               ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback);
                if (ret < 0)
                        goto done;
        }
 
 done:
-       mutex_unlock(&video->ctrl_mutex);
+       mutex_unlock(&chain->ctrl_mutex);
        return ret;
 }
 
-int uvc_ctrl_get(struct uvc_video_device *video,
+int uvc_ctrl_get(struct uvc_video_chain *chain,
        struct v4l2_ext_control *xctrl)
 {
        struct uvc_control *ctrl;
@@ -974,13 +974,13 @@ int uvc_ctrl_get(struct uvc_video_device *video,
        unsigned int i;
        int ret;
 
-       ctrl = uvc_find_control(video, xctrl->id, &mapping);
+       ctrl = uvc_find_control(chain, xctrl->id, &mapping);
        if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0)
                return -EINVAL;
 
        if (!ctrl->loaded) {
-               ret = uvc_query_ctrl(video->dev, UVC_GET_CUR, ctrl->entity->id,
-                               video->dev->intfnum, ctrl->info->selector,
+               ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, ctrl->entity->id,
+                               chain->dev->intfnum, ctrl->info->selector,
                                uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
                                ctrl->info->size);
                if (ret < 0)
@@ -1005,7 +1005,7 @@ int uvc_ctrl_get(struct uvc_video_device *video,
        return 0;
 }
 
-int uvc_ctrl_set(struct uvc_video_device *video,
+int uvc_ctrl_set(struct uvc_video_chain *chain,
        struct v4l2_ext_control *xctrl)
 {
        struct uvc_control *ctrl;
@@ -1013,7 +1013,7 @@ int uvc_ctrl_set(struct uvc_video_device *video,
        s32 value = xctrl->value;
        int ret;
 
-       ctrl = uvc_find_control(video, xctrl->id, &mapping);
+       ctrl = uvc_find_control(chain, xctrl->id, &mapping);
        if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_SET_CUR) == 0)
                return -EINVAL;
 
@@ -1028,8 +1028,8 @@ int uvc_ctrl_set(struct uvc_video_device *video,
                        memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
                                0, ctrl->info->size);
                } else {
-                       ret = uvc_query_ctrl(video->dev, UVC_GET_CUR,
-                               ctrl->entity->id, video->dev->intfnum,
+                       ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
+                               ctrl->entity->id, chain->dev->intfnum,
                                ctrl->info->selector,
                                uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
                                ctrl->info->size);
@@ -1058,7 +1058,7 @@ int uvc_ctrl_set(struct uvc_video_device *video,
  * Dynamic controls
  */
 
-int uvc_xu_ctrl_query(struct uvc_video_device *video,
+int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
        struct uvc_xu_control *xctrl, int set)
 {
        struct uvc_entity *entity;
@@ -1068,7 +1068,7 @@ int uvc_xu_ctrl_query(struct uvc_video_device *video,
        int ret;
 
        /* Find the extension unit. */
-       list_for_each_entry(entity, &video->extensions, chain) {
+       list_for_each_entry(entity, &chain->extensions, chain) {
                if (entity->id == xctrl->unit)
                        break;
        }
@@ -1107,7 +1107,7 @@ int uvc_xu_ctrl_query(struct uvc_video_device *video,
            (!set && !(ctrl->info->flags & UVC_CONTROL_GET_CUR)))
                return -EINVAL;
 
-       if (mutex_lock_interruptible(&video->ctrl_mutex))
+       if (mutex_lock_interruptible(&chain->ctrl_mutex))
                return -ERESTARTSYS;
 
        memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
@@ -1120,8 +1120,8 @@ int uvc_xu_ctrl_query(struct uvc_video_device *video,
                goto out;
        }
 
-       ret = uvc_query_ctrl(video->dev, set ? UVC_SET_CUR : UVC_GET_CUR,
-                            xctrl->unit, video->dev->intfnum, xctrl->selector,
+       ret = uvc_query_ctrl(chain->dev, set ? UVC_SET_CUR : UVC_GET_CUR,
+                            xctrl->unit, chain->dev->intfnum, xctrl->selector,
                             data, xctrl->size);
        if (ret < 0)
                goto out;
@@ -1137,7 +1137,7 @@ out:
                       uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
                       xctrl->size);
 
-       mutex_unlock(&video->ctrl_mutex);
+       mutex_unlock(&chain->ctrl_mutex);
        return ret;
 }
 
index 81bf671..8756be5 100644 (file)
@@ -276,8 +276,20 @@ static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev,
        return NULL;
 }
 
+static struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id)
+{
+       struct uvc_streaming *stream;
+
+       list_for_each_entry(stream, &dev->streams, list) {
+               if (stream->header.bTerminalLink == id)
+                       return stream;
+       }
+
+       return NULL;
+}
+
 /* ------------------------------------------------------------------------
- * Descriptors handling
+ * Descriptors parsing
  */
 
 static int uvc_parse_format(struct uvc_device *dev,
@@ -1160,101 +1172,36 @@ next_descriptor:
 }
 
 /* ------------------------------------------------------------------------
- * USB probe and disconnect
+ * UVC device scan
  */
 
 /*
- * Unregister the video devices.
- */
-static void uvc_unregister_video(struct uvc_device *dev)
-{
-       struct uvc_streaming *streaming;
-
-       list_for_each_entry(streaming, &dev->streams, list) {
-               if (streaming->vdev == NULL)
-                       continue;
-
-               if (streaming->vdev->minor == -1)
-                       video_device_release(streaming->vdev);
-               else
-                       video_unregister_device(streaming->vdev);
-               streaming->vdev = NULL;
-       }
-}
-
-static int uvc_register_video(struct uvc_device *dev,
-               struct uvc_streaming *stream)
-{
-       struct video_device *vdev;
-       struct uvc_entity *term;
-       int ret;
-
-       if (uvc_trace_param & UVC_TRACE_PROBE) {
-               uvc_printk(KERN_INFO, "Found a valid video chain (");
-               list_for_each_entry(term, &dev->video.iterms, chain) {
-                       printk("%d", term->id);
-                       if (term->chain.next != &dev->video.iterms)
-                               printk(",");
-               }
-               printk(" -> %d).\n", dev->video.oterm->id);
-       }
-
-       /* Initialize the streaming interface with default streaming
-        * parameters.
-        */
-       ret = uvc_video_init(stream);
-       if (ret < 0) {
-               uvc_printk(KERN_ERR, "Failed to initialize the device "
-                       "(%d).\n", ret);
-               return ret;
-       }
-
-       /* Register the device with V4L. */
-       vdev = video_device_alloc();
-       if (vdev == NULL)
-               return -1;
-
-       /* We already hold a reference to dev->udev. The video device will be
-        * unregistered before the reference is released, so we don't need to
-        * get another one.
-        */
-       vdev->parent = &dev->intf->dev;
-       vdev->minor = -1;
-       vdev->fops = &uvc_fops;
-       vdev->release = video_device_release;
-       strlcpy(vdev->name, dev->name, sizeof vdev->name);
-
-       /* Set the driver data before calling video_register_device, otherwise
-        * uvc_v4l2_open might race us.
-        */
-       stream->vdev = vdev;
-       video_set_drvdata(vdev, stream);
-
-       if (video_register_device(vdev, VFL_TYPE_GRABBER, -1) < 0) {
-               stream->vdev = NULL;
-               video_device_release(vdev);
-               return -1;
-       }
-
-       return 0;
-}
-
-/*
  * Scan the UVC descriptors to locate a chain starting at an Output Terminal
  * and containing the following units:
  *
- * - one Output Terminal (USB Streaming or Display)
+ * - one or more Output Terminals (USB Streaming or Display)
  * - zero or one Processing Unit
- * - zero, one or mode single-input Selector Units
+ * - zero, one or more single-input Selector Units
  * - zero or one multiple-input Selector Units, provided all inputs are
  *   connected to input terminals
  * - zero, one or mode single-input Extension Units
  * - one or more Input Terminals (Camera, External or USB Streaming)
  *
- * A side forward scan is made on each detected entity to check for additional
- * extension units.
+ * The terminal and units must match on of the following structures:
+ *
+ * ITT_*(0) -> +---------+    +---------+    +---------+ -> TT_STREAMING(0)
+ * ...         | SU{0,1} | -> | PU{0,1} | -> | XU{0,n} |    ...
+ * ITT_*(n) -> +---------+    +---------+    +---------+ -> TT_STREAMING(n)
+ *
+ *                 +---------+    +---------+ -> OTT_*(0)
+ * TT_STREAMING -> | PU{0,1} | -> | XU{0,n} |    ...
+ *                 +---------+    +---------+ -> OTT_*(n)
+ *
+ * The Processing Unit and Extension Units can be in any order. Additional
+ * Extension Units connected to the main chain as single-unit branches are
+ * also supported. Single-input Selector Units are ignored.
  */
-static int uvc_scan_chain_entity(struct uvc_video_device *video,
+static int uvc_scan_chain_entity(struct uvc_video_chain *chain,
        struct uvc_entity *entity)
 {
        switch (UVC_ENTITY_TYPE(entity)) {
@@ -1268,20 +1215,20 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video,
                        return -1;
                }
 
-               list_add_tail(&entity->chain, &video->extensions);
+               list_add_tail(&entity->chain, &chain->extensions);
                break;
 
        case UVC_VC_PROCESSING_UNIT:
                if (uvc_trace_param & UVC_TRACE_PROBE)
                        printk(" <- PU %d", entity->id);
 
-               if (video->processing != NULL) {
+               if (chain->processing != NULL) {
                        uvc_trace(UVC_TRACE_DESCR, "Found multiple "
                                "Processing Units in chain.\n");
                        return -1;
                }
 
-               video->processing = entity;
+               chain->processing = entity;
                break;
 
        case UVC_VC_SELECTOR_UNIT:
@@ -1292,13 +1239,13 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video,
                if (entity->selector.bNrInPins == 1)
                        break;
 
-               if (video->selector != NULL) {
+               if (chain->selector != NULL) {
                        uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector "
                                "Units in chain.\n");
                        return -1;
                }
 
-               video->selector = entity;
+               chain->selector = entity;
                break;
 
        case UVC_ITT_VENDOR_SPECIFIC:
@@ -1307,7 +1254,7 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video,
                if (uvc_trace_param & UVC_TRACE_PROBE)
                        printk(" <- IT %d\n", entity->id);
 
-               list_add_tail(&entity->chain, &video->iterms);
+               list_add_tail(&entity->chain, &chain->iterms);
                break;
 
        case UVC_TT_STREAMING:
@@ -1320,14 +1267,7 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video,
                        return -1;
                }
 
-               if (video->sterm != NULL) {
-                       uvc_trace(UVC_TRACE_DESCR, "Found multiple streaming "
-                               "entities in chain.\n");
-                       return -1;
-               }
-
-               list_add_tail(&entity->chain, &video->iterms);
-               video->sterm = entity;
+               list_add_tail(&entity->chain, &chain->iterms);
                break;
 
        default:
@@ -1339,7 +1279,7 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video,
        return 0;
 }
 
-static int uvc_scan_chain_forward(struct uvc_video_device *video,
+static int uvc_scan_chain_forward(struct uvc_video_chain *chain,
        struct uvc_entity *entity, struct uvc_entity *prev)
 {
        struct uvc_entity *forward;
@@ -1350,28 +1290,51 @@ static int uvc_scan_chain_forward(struct uvc_video_device *video,
        found = 0;
 
        while (1) {
-               forward = uvc_entity_by_reference(video->dev, entity->id,
+               forward = uvc_entity_by_reference(chain->dev, entity->id,
                        forward);
                if (forward == NULL)
                        break;
-
-               if (UVC_ENTITY_TYPE(forward) != UVC_VC_EXTENSION_UNIT ||
-                   forward == prev)
+               if (forward == prev)
                        continue;
 
-               if (forward->extension.bNrInPins != 1) {
-                       uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has "
-                               "more than 1 input pin.\n", entity->id);
-                       return -1;
-               }
+               switch (UVC_ENTITY_TYPE(forward)) {
+               case UVC_VC_EXTENSION_UNIT:
+                       if (forward->extension.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->extensions);
+                       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, &video->extensions);
-               if (uvc_trace_param & UVC_TRACE_PROBE) {
-                       if (!found)
-                               printk(" (-> XU");
+                       list_add_tail(&forward->chain, &chain->oterms);
+                       if (uvc_trace_param & UVC_TRACE_PROBE) {
+                               if (!found)
+                                       printk(" (->");
 
-                       printk(" %d", forward->id);
-                       found = 1;
+                               printk(" OT %d", forward->id);
+                               found = 1;
+                       }
+                       break;
                }
        }
        if (found)
@@ -1380,7 +1343,7 @@ static int uvc_scan_chain_forward(struct uvc_video_device *video,
        return 0;
 }
 
-static int uvc_scan_chain_backward(struct uvc_video_device *video,
+static int uvc_scan_chain_backward(struct uvc_video_chain *chain,
        struct uvc_entity *entity)
 {
        struct uvc_entity *term;
@@ -1405,10 +1368,10 @@ static int uvc_scan_chain_backward(struct uvc_video_device *video,
                if (uvc_trace_param & UVC_TRACE_PROBE)
                        printk(" <- IT");
 
-               video->selector = entity;
+               chain->selector = entity;
                for (i = 0; i < entity->selector.bNrInPins; ++i) {
                        id = entity->selector.baSourceID[i];
-                       term = uvc_entity_by_id(video->dev, id);
+                       term = uvc_entity_by_id(chain->dev, id);
                        if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) {
                                uvc_trace(UVC_TRACE_DESCR, "Selector unit %d "
                                        "input %d isn't connected to an "
@@ -1419,8 +1382,8 @@ static int uvc_scan_chain_backward(struct uvc_video_device *video,
                        if (uvc_trace_param & UVC_TRACE_PROBE)
                                printk(" %d", term->id);
 
-                       list_add_tail(&term->chain, &video->iterms);
-                       uvc_scan_chain_forward(video, term, entity);
+                       list_add_tail(&term->chain, &chain->iterms);
+                       uvc_scan_chain_forward(chain, term, entity);
                }
 
                if (uvc_trace_param & UVC_TRACE_PROBE)
@@ -1433,100 +1396,129 @@ static int uvc_scan_chain_backward(struct uvc_video_device *video,
        return id;
 }
 
-static int uvc_scan_chain(struct uvc_video_device *video)
+static int uvc_scan_chain(struct uvc_video_chain *chain,
+                         struct uvc_entity *oterm)
 {
        struct uvc_entity *entity, *prev;
        int id;
 
-       entity = video->oterm;
+       entity = oterm;
+       list_add_tail(&entity->chain, &chain->oterms);
        uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain: OT %d", entity->id);
 
-       if (UVC_ENTITY_TYPE(entity) == UVC_TT_STREAMING)
-               video->sterm = entity;
-
        id = entity->output.bSourceID;
        while (id != 0) {
                prev = entity;
-               entity = uvc_entity_by_id(video->dev, 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 -1;
+                       return -EINVAL;
+               }
+
+               if (entity->chain.next || entity->chain.prev) {
+                       uvc_trace(UVC_TRACE_DESCR, "Found reference to "
+                               "entity %d already in chain.\n", id);
+                       return -EINVAL;
                }
 
                /* Process entity */
-               if (uvc_scan_chain_entity(video, entity) < 0)
-                       return -1;
+               if (uvc_scan_chain_entity(chain, entity) < 0)
+                       return -EINVAL;
 
                /* Forward scan */
-               if (uvc_scan_chain_forward(video, entity, prev) < 0)
-                       return -1;
+               if (uvc_scan_chain_forward(chain, entity, prev) < 0)
+                       return -EINVAL;
 
                /* Stop when a terminal is found. */
-               if (!UVC_ENTITY_IS_UNIT(entity))
+               if (UVC_ENTITY_IS_TERM(entity))
                        break;
 
                /* Backward scan */
-               id = uvc_scan_chain_backward(video, entity);
+               id = uvc_scan_chain_backward(chain, entity);
                if (id < 0)
                        return id;
        }
 
-       if (video->sterm == NULL) {
-               uvc_trace(UVC_TRACE_DESCR, "No streaming entity found in "
-                       "chain.\n");
-               return -1;
+       return 0;
+}
+
+static unsigned int uvc_print_terms(struct list_head *terms, char *buffer)
+{
+       struct uvc_entity *term;
+       unsigned int nterms = 0;
+       char *p = buffer;
+
+       list_for_each_entry(term, terms, chain) {
+               p += sprintf(p, "%u", term->id);
+               if (term->chain.next != terms) {
+                       p += sprintf(p, ",");
+                       if (++nterms >= 4) {
+                               p += sprintf(p, "...");
+                               break;
+                       }
+               }
        }
 
-       return 0;
+       return p - buffer;
+}
+
+static const char *uvc_print_chain(struct uvc_video_chain *chain)
+{
+       static char buffer[43];
+       char *p = buffer;
+
+       p += uvc_print_terms(&chain->iterms, p);
+       p += sprintf(p, " -> ");
+       uvc_print_terms(&chain->oterms, p);
+
+       return buffer;
 }
 
 /*
  * Scan the device for video chains and register video devices.
  *
- * The driver currently supports a single video device per control interface
- * only. The terminal and units must match the following structure:
- *
- * ITT_* -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> TT_STREAMING
- * TT_STREAMING -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> OTT_*
- *
- * The Extension Units, if present, must have a single input pin. The
- * Processing Unit and Extension Units can be in any order. Additional
- * Extension Units connected to the main chain as single-unit branches are
- * also supported.
+ * Chains are scanned starting at their output terminals and walked backwards.
  */
 static int uvc_scan_device(struct uvc_device *dev)
 {
+       struct uvc_video_chain *chain;
        struct uvc_entity *term;
-       int found = 0;
 
-       /* Check if the control interface matches the structure we expect. */
        list_for_each_entry(term, &dev->entities, list) {
-               struct uvc_streaming *stream;
-
-               if (!UVC_ENTITY_IS_TERM(term) || !UVC_ENTITY_IS_OTERM(term))
+               if (!UVC_ENTITY_IS_OTERM(term))
                        continue;
 
-               memset(&dev->video, 0, sizeof dev->video);
-               mutex_init(&dev->video.ctrl_mutex);
-               INIT_LIST_HEAD(&dev->video.iterms);
-               INIT_LIST_HEAD(&dev->video.extensions);
-               dev->video.oterm = term;
-               dev->video.dev = dev;
-               if (uvc_scan_chain(&dev->video) < 0)
+               /* 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.
+                */
+               if (term->chain.next || term->chain.prev)
                        continue;
 
-               list_for_each_entry(stream, &dev->streams, list) {
-                       if (stream->header.bTerminalLink ==
-                           dev->video.sterm->id) {
-                               uvc_register_video(dev, stream);
-                               found = 1;
-                               break;
-                       }
+               chain = kzalloc(sizeof(*chain), GFP_KERNEL);
+               if (chain == NULL)
+                       return -ENOMEM;
+
+               INIT_LIST_HEAD(&chain->iterms);
+               INIT_LIST_HEAD(&chain->oterms);
+               INIT_LIST_HEAD(&chain->extensions);
+               mutex_init(&chain->ctrl_mutex);
+               chain->dev = dev;
+
+               if (uvc_scan_chain(chain, term) < 0) {
+                       kfree(chain);
+                       continue;
                }
+
+               uvc_trace(UVC_TRACE_PROBE, "Found a valid video chain (%s).\n",
+                         uvc_print_chain(chain));
+
+               list_add_tail(&chain->list, &dev->chains);
        }
 
-       if (!found) {
+       if (list_empty(&dev->chains)) {
                uvc_printk(KERN_INFO, "No valid video chain found.\n");
                return -1;
        }
@@ -1534,6 +1526,133 @@ static int uvc_scan_device(struct uvc_device *dev)
        return 0;
 }
 
+/* ------------------------------------------------------------------------
+ * Video device registration and unregistration
+ */
+
+/*
+ * Unregister the video devices.
+ */
+static void uvc_unregister_video(struct uvc_device *dev)
+{
+       struct uvc_streaming *stream;
+
+       list_for_each_entry(stream, &dev->streams, list) {
+               if (stream->vdev == NULL)
+                       continue;
+
+               if (stream->vdev->minor == -1)
+                       video_device_release(stream->vdev);
+               else
+                       video_unregister_device(stream->vdev);
+               stream->vdev = NULL;
+       }
+}
+
+static int uvc_register_video(struct uvc_device *dev,
+               struct uvc_streaming *stream)
+{
+       struct video_device *vdev;
+       int ret;
+
+       /* Initialize the streaming interface with default streaming
+        * parameters.
+        */
+       ret = uvc_video_init(stream);
+       if (ret < 0) {
+               uvc_printk(KERN_ERR, "Failed to initialize the device "
+                       "(%d).\n", ret);
+               return ret;
+       }
+
+       /* Register the device with V4L. */
+       vdev = video_device_alloc();
+       if (vdev == NULL) {
+               uvc_printk(KERN_ERR, "Failed to allocate video device (%d).\n",
+                          ret);
+               return -ENOMEM;
+       }
+
+       /* We already hold a reference to dev->udev. The video device will be
+        * unregistered before the reference is released, so we don't need to
+        * get another one.
+        */
+       vdev->parent = &dev->intf->dev;
+       vdev->minor = -1;
+       vdev->fops = &uvc_fops;
+       vdev->release = video_device_release;
+       strlcpy(vdev->name, dev->name, sizeof vdev->name);
+
+       /* Set the driver data before calling video_register_device, otherwise
+        * uvc_v4l2_open might race us.
+        */
+       stream->vdev = vdev;
+       video_set_drvdata(vdev, stream);
+
+       ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+       if (ret < 0) {
+               uvc_printk(KERN_ERR, "Failed to register video device (%d).\n",
+                          ret);
+               stream->vdev = NULL;
+               video_device_release(vdev);
+               return ret;
+       }
+
+       return 0;
+}
+
+/*
+ * Register all video devices in all chains.
+ */
+static int uvc_register_terms(struct uvc_device *dev,
+       struct uvc_video_chain *chain, struct list_head *terms)
+{
+       struct uvc_streaming *stream;
+       struct uvc_entity *term;
+       int ret;
+
+       list_for_each_entry(term, terms, chain) {
+               if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING)
+                       continue;
+
+               stream = uvc_stream_by_id(dev, term->id);
+               if (stream == NULL) {
+                       uvc_printk(KERN_INFO, "No streaming interface found "
+                                  "for terminal %u.", term->id);
+                       continue;
+               }
+
+               stream->chain = chain;
+               ret = uvc_register_video(dev, stream);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int uvc_register_chains(struct uvc_device *dev)
+{
+       struct uvc_video_chain *chain;
+       int ret;
+
+       list_for_each_entry(chain, &dev->chains, list) {
+               ret = uvc_register_terms(dev, chain, &chain->iterms);
+               if (ret < 0)
+                       return ret;
+
+               ret = uvc_register_terms(dev, chain, &chain->oterms);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+/* ------------------------------------------------------------------------
+ * USB probe, disconnect, suspend and resume
+ */
+
 /*
  * Delete the UVC device.
  *
@@ -1555,7 +1674,7 @@ void uvc_delete(struct kref *kref)
        struct uvc_device *dev = container_of(kref, struct uvc_device, kref);
        struct list_head *p, *n;
 
-       /* Unregister the video device. */
+       /* Unregister the video devices. */
        uvc_unregister_video(dev);
        usb_put_intf(dev->intf);
        usb_put_dev(dev->udev);
@@ -1563,6 +1682,12 @@ void uvc_delete(struct kref *kref)
        uvc_status_cleanup(dev);
        uvc_ctrl_cleanup_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);
+       }
+
        list_for_each_safe(p, n, &dev->entities) {
                struct uvc_entity *entity;
                entity = list_entry(p, struct uvc_entity, list);
@@ -1603,6 +1728,7 @@ static int uvc_probe(struct usb_interface *intf,
                return -ENOMEM;
 
        INIT_LIST_HEAD(&dev->entities);
+       INIT_LIST_HEAD(&dev->chains);
        INIT_LIST_HEAD(&dev->streams);
        kref_init(&dev->kref);
        atomic_set(&dev->users, 0);
@@ -1644,10 +1770,14 @@ static int uvc_probe(struct usb_interface *intf,
        if (uvc_ctrl_init_device(dev) < 0)
                goto error;
 
-       /* Scan the device for video chains and register video devices. */
+       /* Scan the device for video chains. */
        if (uvc_scan_device(dev) < 0)
                goto error;
 
+       /* Register video devices. */
+       if (uvc_register_chains(dev) < 0)
+               goto error;
+
        /* Save our data pointer in the interface data. */
        usb_set_intfdata(intf, dev);
 
index 7e847bb..9e73515 100644 (file)
@@ -40,7 +40,7 @@
  * table for the controls that can be mapped directly, and handle the others
  * manually.
  */
-static int uvc_v4l2_query_menu(struct uvc_video_device *video,
+static int uvc_v4l2_query_menu(struct uvc_video_chain *chain,
        struct v4l2_querymenu *query_menu)
 {
        struct uvc_menu_info *menu_info;
@@ -49,7 +49,7 @@ static int uvc_v4l2_query_menu(struct uvc_video_device *video,
        u32 index = query_menu->index;
        u32 id = query_menu->id;
 
-       ctrl = uvc_find_control(video, query_menu->id, &mapping);
+       ctrl = uvc_find_control(chain, query_menu->id, &mapping);
        if (ctrl == NULL || mapping->v4l2_type != V4L2_CTRL_TYPE_MENU)
                return -EINVAL;
 
@@ -451,7 +451,7 @@ static int uvc_v4l2_open(struct file *file)
                }
        }
 
-       handle->video = &stream->dev->video;
+       handle->chain = stream->chain;
        handle->stream = stream;
        handle->state = UVC_HANDLE_PASSIVE;
        file->private_data = handle;
@@ -498,7 +498,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
 {
        struct video_device *vdev = video_devdata(file);
        struct uvc_fh *handle = (struct uvc_fh *)file->private_data;
-       struct uvc_video_device *video = handle->video;
+       struct uvc_video_chain *chain = handle->chain;
        struct uvc_streaming *stream = handle->stream;
        long ret = 0;
 
@@ -525,7 +525,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
 
        /* Get, Set & Query control */
        case VIDIOC_QUERYCTRL:
-               return uvc_query_v4l2_ctrl(video, arg);
+               return uvc_query_v4l2_ctrl(chain, arg);
 
        case VIDIOC_G_CTRL:
        {
@@ -535,12 +535,12 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
                memset(&xctrl, 0, sizeof xctrl);
                xctrl.id = ctrl->id;
 
-              ret = uvc_ctrl_begin(video);
-              if (ret < 0)
+               ret = uvc_ctrl_begin(chain);
+               if (ret < 0)
                        return ret;
 
-               ret = uvc_ctrl_get(video, &xctrl);
-               uvc_ctrl_rollback(video);
+               ret = uvc_ctrl_get(chain, &xctrl);
+               uvc_ctrl_rollback(chain);
                if (ret >= 0)
                        ctrl->value = xctrl.value;
                break;
@@ -555,21 +555,21 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
                xctrl.id = ctrl->id;
                xctrl.value = ctrl->value;
 
-              ret = uvc_ctrl_begin(video);
-              if (ret < 0)
+               uvc_ctrl_begin(chain);
+               if (ret < 0)
                        return ret;
 
-               ret = uvc_ctrl_set(video, &xctrl);
+               ret = uvc_ctrl_set(chain, &xctrl);
                if (ret < 0) {
-                       uvc_ctrl_rollback(video);
+                       uvc_ctrl_rollback(chain);
                        return ret;
                }
-               ret = uvc_ctrl_commit(video);
+               ret = uvc_ctrl_commit(chain);
                break;
        }
 
        case VIDIOC_QUERYMENU:
-               return uvc_v4l2_query_menu(video, arg);
+               return uvc_v4l2_query_menu(chain, arg);
 
        case VIDIOC_G_EXT_CTRLS:
        {
@@ -577,20 +577,20 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
                struct v4l2_ext_control *ctrl = ctrls->controls;
                unsigned int i;
 
-              ret = uvc_ctrl_begin(video);
-              if (ret < 0)
+               ret = uvc_ctrl_begin(chain);
+               if (ret < 0)
                        return ret;
 
                for (i = 0; i < ctrls->count; ++ctrl, ++i) {
-                       ret = uvc_ctrl_get(video, ctrl);
+                       ret = uvc_ctrl_get(chain, ctrl);
                        if (ret < 0) {
-                               uvc_ctrl_rollback(video);
+                               uvc_ctrl_rollback(chain);
                                ctrls->error_idx = i;
                                return ret;
                        }
                }
                ctrls->error_idx = 0;
-               ret = uvc_ctrl_rollback(video);
+               ret = uvc_ctrl_rollback(chain);
                break;
        }
 
@@ -601,14 +601,14 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
                struct v4l2_ext_control *ctrl = ctrls->controls;
                unsigned int i;
 
-               ret = uvc_ctrl_begin(video);
+               ret = uvc_ctrl_begin(chain);
                if (ret < 0)
                        return ret;
 
                for (i = 0; i < ctrls->count; ++ctrl, ++i) {
-                       ret = uvc_ctrl_set(video, ctrl);
+                       ret = uvc_ctrl_set(chain, ctrl);
                        if (ret < 0) {
-                               uvc_ctrl_rollback(video);
+                               uvc_ctrl_rollback(chain);
                                ctrls->error_idx = i;
                                return ret;
                        }
@@ -617,31 +617,31 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
                ctrls->error_idx = 0;
 
                if (cmd == VIDIOC_S_EXT_CTRLS)
-                       ret = uvc_ctrl_commit(video);
+                       ret = uvc_ctrl_commit(chain);
                else
-                       ret = uvc_ctrl_rollback(video);
+                       ret = uvc_ctrl_rollback(chain);
                break;
        }
 
        /* Get, Set & Enum input */
        case VIDIOC_ENUMINPUT:
        {
-               const struct uvc_entity *selector = video->selector;
+               const struct uvc_entity *selector = chain->selector;
                struct v4l2_input *input = arg;
                struct uvc_entity *iterm = NULL;
                u32 index = input->index;
                int pin = 0;
 
                if (selector == NULL ||
-                   (video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
+                   (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
                        if (index != 0)
                                return -EINVAL;
-                       iterm = list_first_entry(&video->iterms,
+                       iterm = list_first_entry(&chain->iterms,
                                        struct uvc_entity, chain);
                        pin = iterm->id;
                } else if (pin < selector->selector.bNrInPins) {
                        pin = selector->selector.baSourceID[index];
-                       list_for_each_entry(iterm, video->iterms.next, chain) {
+                       list_for_each_entry(iterm, chain->iterms.next, chain) {
                                if (iterm->id == pin)
                                        break;
                        }
@@ -662,14 +662,14 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
        {
                u8 input;
 
-               if (video->selector == NULL ||
-                   (video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
+               if (chain->selector == NULL ||
+                   (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
                        *(int *)arg = 0;
                        break;
                }
 
-               ret = uvc_query_ctrl(video->dev, UVC_GET_CUR,
-                       video->selector->id, video->dev->intfnum,
+               ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
+                       chain->selector->id, chain->dev->intfnum,
                        UVC_SU_INPUT_SELECT_CONTROL, &input, 1);
                if (ret < 0)
                        return ret;
@@ -685,18 +685,18 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
                if ((ret = uvc_acquire_privileges(handle)) < 0)
                        return ret;
 
-               if (video->selector == NULL ||
-                   (video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
+               if (chain->selector == NULL ||
+                   (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
                        if (input != 1)
                                return -EINVAL;
                        break;
                }
 
-               if (input == 0 || input > video->selector->selector.bNrInPins)
+               if (input == 0 || input > chain->selector->selector.bNrInPins)
                        return -EINVAL;
 
-               return uvc_query_ctrl(video->dev, UVC_SET_CUR,
-                       video->selector->id, video->dev->intfnum,
+               return uvc_query_ctrl(chain->dev, UVC_SET_CUR,
+                       chain->selector->id, chain->dev->intfnum,
                        UVC_SU_INPUT_SELECT_CONTROL, &input, 1);
        }
 
@@ -1019,10 +1019,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
        }
 
        case UVCIOC_CTRL_GET:
-               return uvc_xu_ctrl_query(video, arg, 0);
+               return uvc_xu_ctrl_query(chain, arg, 0);
 
        case UVCIOC_CTRL_SET:
-               return uvc_xu_ctrl_query(video, arg, 1);
+               return uvc_xu_ctrl_query(chain, arg, 1);
 
        default:
                if ((ret = v4l_compat_translate_ioctl(file, cmd, arg,
index acbd73a..5b757f3 100644 (file)
@@ -128,7 +128,7 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream,
        if (data == NULL)
                return -ENOMEM;
 
-       if ((video->dev->quirks & UVC_QUIRK_PROBE_DEF) && query == UVC_GET_DEF)
+       if ((stream->dev->quirks & UVC_QUIRK_PROBE_DEF) && query == UVC_GET_DEF)
                return -EIO;
 
        ret = __uvc_query_ctrl(stream->dev, query, 0, stream->intfnum,
index 3cd9041..e7958aa 100644 (file)
@@ -80,9 +80,11 @@ struct uvc_xu_control {
 #define UVC_ENTITY_IS_UNIT(entity)     (((entity)->type & 0xff00) == 0)
 #define UVC_ENTITY_IS_TERM(entity)     (((entity)->type & 0xff00) != 0)
 #define UVC_ENTITY_IS_ITERM(entity) \
-       (((entity)->type & 0x8000) == UVC_TERM_INPUT)
+       (UVC_ENTITY_IS_TERM(entity) && \
+       ((entity)->type & 0x8000) == UVC_TERM_INPUT)
 #define UVC_ENTITY_IS_OTERM(entity) \
-       (((entity)->type & 0x8000) == UVC_TERM_OUTPUT)
+       (UVC_ENTITY_IS_TERM(entity) && \
+       ((entity)->type & 0x8000) == UVC_TERM_OUTPUT)
 
 
 /* ------------------------------------------------------------------------
@@ -402,10 +404,24 @@ struct uvc_video_queue {
        struct list_head irqqueue;
 };
 
+struct uvc_video_chain {
+       struct uvc_device *dev;
+       struct list_head list;
+
+       struct list_head iterms;                /* Input terminals */
+       struct list_head oterms;                /* Output terminals */
+       struct uvc_entity *processing;          /* Processing unit */
+       struct uvc_entity *selector;            /* Selector unit */
+       struct list_head extensions;            /* Extension units */
+
+       struct mutex ctrl_mutex;
+};
+
 struct uvc_streaming {
        struct list_head list;
        struct uvc_device *dev;
        struct video_device *vdev;
+       struct uvc_video_chain *chain;
        atomic_t active;
 
        struct usb_interface *intf;
@@ -446,18 +462,6 @@ struct uvc_streaming {
        __u8 last_fid;
 };
 
-struct uvc_video_device {
-       struct uvc_device *dev;
-
-       struct list_head iterms;                /* Input terminals */
-       struct uvc_entity *oterm;               /* Output terminal */
-       struct uvc_entity *sterm;               /* USB streaming terminal */
-       struct uvc_entity *processing;
-       struct uvc_entity *selector;
-       struct list_head extensions;
-       struct mutex ctrl_mutex;
-};
-
 enum uvc_device_state {
        UVC_DEV_DISCONNECTED = 1,
 };
@@ -480,8 +484,7 @@ struct uvc_device {
        __u32 clock_frequency;
 
        struct list_head entities;
-
-       struct uvc_video_device video;
+       struct list_head chains;
 
        /* Video Streaming interfaces */
        struct list_head streams;
@@ -500,7 +503,7 @@ enum uvc_handle_state {
 };
 
 struct uvc_fh {
-       struct uvc_video_device *video;
+       struct uvc_video_chain *chain;
        struct uvc_streaming *stream;
        enum uvc_handle_state state;
 };
@@ -618,9 +621,9 @@ extern int uvc_status_suspend(struct uvc_device *dev);
 extern int uvc_status_resume(struct uvc_device *dev);
 
 /* Controls */
-extern struct uvc_control *uvc_find_control(struct uvc_video_device *video,
+extern struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
                __u32 v4l2_id, struct uvc_control_mapping **mapping);
-extern int uvc_query_v4l2_ctrl(struct uvc_video_device *video,
+extern int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
                struct v4l2_queryctrl *v4l2_ctrl);
 
 extern int uvc_ctrl_add_info(struct uvc_control_info *info);
@@ -630,23 +633,23 @@ extern void uvc_ctrl_cleanup_device(struct uvc_device *dev);
 extern int uvc_ctrl_resume_device(struct uvc_device *dev);
 extern void uvc_ctrl_init(void);
 
-extern int uvc_ctrl_begin(struct uvc_video_device *video);
-extern int __uvc_ctrl_commit(struct uvc_video_device *video, int rollback);
-static inline int uvc_ctrl_commit(struct uvc_video_device *video)
+extern int uvc_ctrl_begin(struct uvc_video_chain *chain);
+extern int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback);
+static inline int uvc_ctrl_commit(struct uvc_video_chain *chain)
 {
-       return __uvc_ctrl_commit(video, 0);
+       return __uvc_ctrl_commit(chain, 0);
 }
-static inline int uvc_ctrl_rollback(struct uvc_video_device *video)
+static inline int uvc_ctrl_rollback(struct uvc_video_chain *chain)
 {
-       return __uvc_ctrl_commit(video, 1);
+       return __uvc_ctrl_commit(chain, 1);
 }
 
-extern int uvc_ctrl_get(struct uvc_video_device *video,
+extern int uvc_ctrl_get(struct uvc_video_chain *chain,
                struct v4l2_ext_control *xctrl);
-extern int uvc_ctrl_set(struct uvc_video_device *video,
+extern int uvc_ctrl_set(struct uvc_video_chain *chain,
                struct v4l2_ext_control *xctrl);
 
-extern int uvc_xu_ctrl_query(struct uvc_video_device *video,
+extern int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
                struct uvc_xu_control *ctrl, int set);
 
 /* Utility functions */