[media] uvcvideo: Delay initialization of XU controls
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Wed, 29 Sep 2010 19:03:03 +0000 (16:03 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Thu, 21 Oct 2010 03:18:24 +0000 (01:18 -0200)
XU controls initialization requires querying the device for control
information. As some buggy UVC devices will crash when queried
repeatedly in a tight loop, delay XU controls initialization until first
use.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/uvc/uvc_ctrl.c

index 97a2395..a0c9d58 100644 (file)
@@ -1164,6 +1164,90 @@ int uvc_ctrl_set(struct uvc_video_chain *chain,
  * Dynamic controls
  */
 
+/*
+ * Query control information (size and flags) for XU controls.
+ */
+static int uvc_ctrl_fill_xu_info(struct uvc_device *dev,
+       const struct uvc_control *ctrl, struct uvc_control_info *info)
+{
+       u8 *data;
+       int ret;
+
+       data = kmalloc(2, GFP_KERNEL);
+       if (data == NULL)
+               return -ENOMEM;
+
+       memcpy(info->entity, ctrl->entity->extension.guidExtensionCode,
+              sizeof(info->entity));
+       info->index = ctrl->index;
+       info->selector = ctrl->index + 1;
+
+       /* Query and verify the control length (GET_LEN) */
+       ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id, dev->intfnum,
+                            info->selector, data, 2);
+       if (ret < 0) {
+               uvc_trace(UVC_TRACE_CONTROL,
+                         "GET_LEN failed on control %pUl/%u (%d).\n",
+                          info->entity, info->selector, ret);
+               goto done;
+       }
+
+       info->size = le16_to_cpup((__le16 *)data);
+
+       /* Query the control information (GET_INFO) */
+       ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, dev->intfnum,
+                            info->selector, data, 1);
+       if (ret < 0) {
+               uvc_trace(UVC_TRACE_CONTROL,
+                         "GET_INFO failed on control %pUl/%u (%d).\n",
+                         info->entity, info->selector, ret);
+               goto done;
+       }
+
+       info->flags = UVC_CONTROL_GET_MIN | UVC_CONTROL_GET_MAX
+                   | UVC_CONTROL_GET_RES | UVC_CONTROL_GET_DEF
+                   | (data[0] & UVC_CONTROL_CAP_GET ? UVC_CONTROL_GET_CUR : 0)
+                   | (data[0] & UVC_CONTROL_CAP_SET ? UVC_CONTROL_SET_CUR : 0)
+                   | (data[0] & UVC_CONTROL_CAP_AUTOUPDATE ?
+                      UVC_CONTROL_AUTO_UPDATE : 0);
+
+       uvc_trace(UVC_TRACE_CONTROL, "XU control %pUl/%u queried: len %u, "
+                 "flags { get %u set %u auto %u }.\n",
+                 info->entity, info->selector, info->size,
+                 (info->flags & UVC_CONTROL_GET_CUR) ? 1 : 0,
+                 (info->flags & UVC_CONTROL_SET_CUR) ? 1 : 0,
+                 (info->flags & UVC_CONTROL_AUTO_UPDATE) ? 1 : 0);
+
+done:
+       kfree(data);
+       return ret;
+}
+
+static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl,
+       const struct uvc_control_info *info);
+
+static int uvc_ctrl_init_xu_ctrl(struct uvc_device *dev,
+       struct uvc_control *ctrl)
+{
+       struct uvc_control_info info;
+       int ret;
+
+       if (ctrl->initialized)
+               return 0;
+
+       ret = uvc_ctrl_fill_xu_info(dev, ctrl, &info);
+       if (ret < 0)
+               return ret;
+
+       ret = uvc_ctrl_add_info(dev, ctrl, &info);
+       if (ret < 0)
+               uvc_trace(UVC_TRACE_CONTROL, "Failed to initialize control "
+                         "%pUl/%u on device %s entity %u\n", info.entity,
+                         info.selector, dev->udev->devpath, ctrl->entity->id);
+
+       return ret;
+}
+
 int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
        struct uvc_xu_control *xctrl, int set)
 {
@@ -1186,13 +1270,10 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
                return -EINVAL;
        }
 
-       /* Find the control. */
+       /* Find the control and perform delayed initialization if needed. */
        for (i = 0; i < entity->ncontrols; ++i) {
                ctrl = &entity->controls[i];
-               if (!ctrl->initialized)
-                       continue;
-
-               if (ctrl->info.selector == xctrl->selector) {
+               if (ctrl->index == xctrl->selector - 1) {
                        found = 1;
                        break;
                }
@@ -1204,6 +1285,10 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
                return -EINVAL;
        }
 
+       ret = uvc_ctrl_init_xu_ctrl(chain->dev, ctrl);
+       if (ret < 0)
+               return -ENOENT;
+
        /* Validate control data size. */
        if (ctrl->info.size != xctrl->size)
                return -EINVAL;
@@ -1294,65 +1379,6 @@ int uvc_ctrl_resume_device(struct uvc_device *dev)
  * Control and mapping handling
  */
 
-/*
- * Query control information (size and flags) for XU controls.
- */
-static int uvc_ctrl_fill_xu_info(struct uvc_device *dev,
-       const struct uvc_control *ctrl, struct uvc_control_info *info)
-{
-       u8 *data;
-       int ret;
-
-       data = kmalloc(2, GFP_KERNEL);
-       if (data == NULL)
-               return -ENOMEM;
-
-       memcpy(info->entity, ctrl->entity->extension.guidExtensionCode,
-              sizeof(info->entity));
-       info->index = ctrl->index;
-       info->selector = ctrl->index + 1;
-
-       /* Query and verify the control length (GET_LEN) */
-       ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id, dev->intfnum,
-                            info->selector, data, 2);
-       if (ret < 0) {
-               uvc_trace(UVC_TRACE_CONTROL,
-                         "GET_LEN failed on control %pUl/%u (%d).\n",
-                          info->entity, info->selector, ret);
-               goto done;
-       }
-
-       info->size = le16_to_cpup((__le16 *)data);
-
-       /* Query the control information (GET_INFO) */
-       ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, dev->intfnum,
-                            info->selector, data, 1);
-       if (ret < 0) {
-               uvc_trace(UVC_TRACE_CONTROL,
-                         "GET_INFO failed on control %pUl/%u (%d).\n",
-                         info->entity, info->selector, ret);
-               goto done;
-       }
-
-       info->flags = UVC_CONTROL_GET_MIN | UVC_CONTROL_GET_MAX
-                   | UVC_CONTROL_GET_RES | UVC_CONTROL_GET_DEF
-                   | (data[0] & UVC_CONTROL_CAP_GET ? UVC_CONTROL_GET_CUR : 0)
-                   | (data[0] & UVC_CONTROL_CAP_SET ? UVC_CONTROL_SET_CUR : 0)
-                   | (data[0] & UVC_CONTROL_CAP_AUTOUPDATE ?
-                      UVC_CONTROL_AUTO_UPDATE : 0);
-
-       uvc_trace(UVC_TRACE_CONTROL, "XU control %pUl/%u queried: len %u, "
-                 "flags { get %u set %u auto %u }.\n",
-                 info->entity, info->selector, info->size,
-                 (info->flags & UVC_CONTROL_GET_CUR) ? 1 : 0,
-                 (info->flags & UVC_CONTROL_SET_CUR) ? 1 : 0,
-                 (info->flags & UVC_CONTROL_AUTO_UPDATE) ? 1 : 0);
-
-done:
-       kfree(data);
-       return ret;
-}
-
 /*
  * Add control information to a given control.
  */
@@ -1434,7 +1460,7 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
 
        if (mapping->id & ~V4L2_CTRL_ID_MASK) {
                uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', control "
-                       "control id 0x%08x is invalid.\n", mapping->name,
+                       "id 0x%08x is invalid.\n", mapping->name,
                        mapping->id);
                return -EINVAL;
        }
@@ -1443,13 +1469,13 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
        list_for_each_entry(entity, &dev->entities, list) {
                unsigned int i;
 
-               if (!uvc_entity_match_guid(entity, mapping->entity))
+               if (UVC_ENTITY_TYPE(entity) != UVC_VC_EXTENSION_UNIT ||
+                   !uvc_entity_match_guid(entity, mapping->entity))
                        continue;
 
                for (i = 0; i < entity->ncontrols; ++i) {
                        ctrl = &entity->controls[i];
-                       if (ctrl->initialized &&
-                           ctrl->info.selector == mapping->selector) {
+                       if (ctrl->index == mapping->selector - 1) {
                                found = 1;
                                break;
                        }
@@ -1464,6 +1490,13 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
        if (mutex_lock_interruptible(&chain->ctrl_mutex))
                return -ERESTARTSYS;
 
+       /* Perform delayed initialization of XU controls */
+       ret = uvc_ctrl_init_xu_ctrl(dev, ctrl);
+       if (ret < 0) {
+               ret = -ENOENT;
+               goto done;
+       }
+
        list_for_each_entry(map, &ctrl->info.mappings, list) {
                if (mapping->id == map->id) {
                        uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', "
@@ -1567,26 +1600,13 @@ static void uvc_ctrl_init_ctrl(struct uvc_device *dev, struct uvc_control *ctrl)
        const struct uvc_control_mapping *mend =
                mapping + ARRAY_SIZE(uvc_ctrl_mappings);
 
-       /* Query XU controls for control information */
-       if (UVC_ENTITY_TYPE(ctrl->entity) == UVC_VC_EXTENSION_UNIT) {
-               struct uvc_control_info info;
-               int ret;
-
-               ret = uvc_ctrl_fill_xu_info(dev, ctrl, &info);
-               if (ret < 0)
-                       return;
-
-               ret = uvc_ctrl_add_info(dev, ctrl, &info);
-               if (ret < 0) {
-                       /* Skip the control */
-                       uvc_trace(UVC_TRACE_CONTROL, "Failed to initialize "
-                               "control %pUl/%u on device %s entity %u\n",
-                               info.entity, info.selector, dev->udev->devpath,
-                               ctrl->entity->id);
-                       memset(ctrl, 0, sizeof(*ctrl));
-               }
+       /* XU controls initialization requires querying the device for control
+        * information. As some buggy UVC devices will crash when queried
+        * repeatedly in a tight loop, delay XU controls initialization until
+        * first use.
+        */
+       if (UVC_ENTITY_TYPE(ctrl->entity) == UVC_VC_EXTENSION_UNIT)
                return;
-       }
 
        for (; info < iend; ++info) {
                if (uvc_entity_match_guid(ctrl->entity, info->entity) &&