[media] uvcvideo: Hardcode the index/selector relationship for XU controls

Devices advertise XU controls using a bitmask, in which each bit
corresponds to a control. The control selector, used to query the
control, isn't available in the USB descriptors.

All known UVC devices use control selectors equal to the control bit
index plus one. Hardcode that relationship in the driver, making the
UVCIOC_CTRL_ADD ioctl obsolete. All necessary information about XU
controls can be obtained by the driver at enumeration time.

The UVCIOC_CTRL_ADD ioctl is still supported for compatibility reasons,
but now always returns -EEXIST.

Finally, control mappings are now on a per-device basis and no longer
global.

As this changes the userspace interface, bump the driver version number
to 1.0.0 (it was about time).

Signed-off-by: Martin Rubli <martin_rubli@logitech.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c
index e7acf55..2c81b7f 100644
--- a/drivers/media/video/uvc/uvc_ctrl.c
+++ b/drivers/media/video/uvc/uvc_ctrl.c
@@ -1294,23 +1294,166 @@
  * Control and mapping handling
  */
 
-static int uvc_ctrl_add_ctrl(struct uvc_device *dev,
-	struct uvc_control_info *info)
+/*
+ * 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)
 {
-	struct uvc_entity *entity;
-	struct uvc_control *ctrl = NULL;
-	int ret = 0, found = 0;
-	unsigned int i;
-	u8 *uvc_info;
-	u8 *uvc_data;
+	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.
+ */
+static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl,
+	const struct uvc_control_info *info)
+{
+	int ret = 0;
+
+	/* Clone the control info struct for this device's instance */
+	ctrl->info = kmemdup(info, sizeof(*info), GFP_KERNEL);
+	if (ctrl->info == NULL) {
+		ret = -ENOMEM;
+		goto done;
+	}
+	INIT_LIST_HEAD(&ctrl->info->mappings);
+
+	/* Allocate an array to save control values (cur, def, max, etc.) */
+	ctrl->uvc_data = kzalloc(ctrl->info->size * UVC_CTRL_DATA_LAST + 1,
+				 GFP_KERNEL);
+	if (ctrl->uvc_data == NULL) {
+		ret = -ENOMEM;
+		goto done;
+	}
+
+	uvc_trace(UVC_TRACE_CONTROL, "Added control %pUl/%u to device %s "
+		"entity %u\n", ctrl->info->entity, ctrl->info->selector,
+		dev->udev->devpath, ctrl->entity->id);
+
+done:
+	if (ret < 0) {
+		kfree(ctrl->uvc_data);
+		kfree(ctrl->info);
+	}
+	return ret;
+}
+
+/*
+ * Add a control mapping to a given control.
+ */
+static int __uvc_ctrl_add_mapping(struct uvc_device *dev,
+	struct uvc_control *ctrl, const struct uvc_control_mapping *mapping)
+{
+	struct uvc_control_mapping *map;
+	unsigned int size;
+
+	/* Most mappings come from static kernel data and need to be duplicated.
+	 * Mappings that come from userspace will be unnecessarily duplicated,
+	 * this could be optimized.
+	 */
+	map = kmemdup(mapping, sizeof(*mapping), GFP_KERNEL);
+	if (map == NULL)
+		return -ENOMEM;
+
+	size = sizeof(*mapping->menu_info) * mapping->menu_count;
+	map->menu_info = kmemdup(mapping->menu_info, size, GFP_KERNEL);
+	if (map->menu_info == NULL) {
+		kfree(map);
+		return -ENOMEM;
+	}
+
+	if (map->get == NULL)
+		map->get = uvc_get_le_value;
+	if (map->set == NULL)
+		map->set = uvc_set_le_value;
+
+	map->ctrl = ctrl->info;
+	list_add_tail(&map->list, &ctrl->info->mappings);
+	uvc_trace(UVC_TRACE_CONTROL,
+		"Adding mapping '%s' to control %pUl/%u.\n",
+		map->name, ctrl->info->entity, ctrl->info->selector);
+
+	return 0;
+}
+
+int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
+	const struct uvc_control_mapping *mapping)
+{
+	struct uvc_device *dev = chain->dev;
+	struct uvc_control_mapping *map;
+	struct uvc_entity *entity;
+	struct uvc_control *ctrl;
+	int found = 0;
+
+	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,
+			mapping->id);
+		return -EINVAL;
+	}
+
+	/* Search for the matching (GUID/CS) control in the given device */
 	list_for_each_entry(entity, &dev->entities, list) {
-		if (!uvc_entity_match_guid(entity, info->entity))
+		unsigned int i;
+
+		if (!uvc_entity_match_guid(entity, mapping->entity))
 			continue;
 
 		for (i = 0; i < entity->ncontrols; ++i) {
 			ctrl = &entity->controls[i];
-			if (ctrl->index == info->index) {
+			if (ctrl->info != NULL &&
+			    ctrl->info->selector == mapping->selector) {
 				found = 1;
 				break;
 			}
@@ -1319,176 +1462,25 @@
 		if (found)
 			break;
 	}
-
 	if (!found)
-		return 0;
+		return -ENOENT;
 
-	uvc_data = kmalloc(info->size * UVC_CTRL_DATA_LAST + 1, GFP_KERNEL);
-	if (uvc_data == NULL)
-		return -ENOMEM;
+	if (mutex_lock_interruptible(&chain->ctrl_mutex))
+		return -ERESTARTSYS;
 
-	uvc_info = uvc_data + info->size * UVC_CTRL_DATA_LAST;
-
-	if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT) {
-		/* Check if the device control information and length match
-		 * the user supplied information.
-		 */
-		ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id,
-				     dev->intfnum, info->selector, uvc_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;
-		}
-
-		if (info->size != le16_to_cpu(*(__le16 *)uvc_data)) {
-			uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u size "
-				"doesn't match user supplied value.\n",
-				info->entity, info->selector);
-			ret = -EINVAL;
-			goto done;
-		}
-
-		ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id,
-				     dev->intfnum, info->selector, uvc_info, 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;
-		}
-
-		if (((info->flags & UVC_CONTROL_GET_CUR) &&
-		    !(*uvc_info & UVC_CONTROL_CAP_GET)) ||
-		    ((info->flags & UVC_CONTROL_SET_CUR) &&
-		    !(*uvc_info & UVC_CONTROL_CAP_SET))) {
-			uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u flags "
-				"don't match supported operations.\n",
-				info->entity, info->selector);
-			ret = -EINVAL;
+	list_for_each_entry(map, &ctrl->info->mappings, list) {
+		if (mapping->id == map->id) {
+			uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', "
+				"control id 0x%08x already exists.\n",
+				mapping->name, mapping->id);
+			ret = -EEXIST;
 			goto done;
 		}
 	}
 
-	ctrl->info = info;
-	ctrl->uvc_data = uvc_data;
-	ctrl->uvc_info = uvc_info;
-
-	uvc_trace(UVC_TRACE_CONTROL, "Added control %pUl/%u to device %s "
-		"entity %u\n", ctrl->info->entity, ctrl->info->selector,
-		dev->udev->devpath, entity->id);
-
+	ret = __uvc_ctrl_add_mapping(dev, ctrl, mapping);
 done:
-	if (ret < 0)
-		kfree(uvc_data);
-
-	return ret;
-}
-
-/*
- * Add an item to the UVC control information list, and instantiate a control
- * structure for each device that supports the control.
- */
-int uvc_ctrl_add_info(struct uvc_control_info *info)
-{
-	struct uvc_control_info *ctrl;
-	struct uvc_device *dev;
-	int ret = 0;
-
-	/* Find matching controls by walking the devices, entities and
-	 * controls list.
-	 */
-	mutex_lock(&uvc_driver.ctrl_mutex);
-
-	/* First check if the list contains a control matching the new one.
-	 * Bail out if it does.
-	 */
-	list_for_each_entry(ctrl, &uvc_driver.controls, list) {
-		if (memcmp(ctrl->entity, info->entity, 16))
-			continue;
-
-		if (ctrl->selector == info->selector) {
-			uvc_trace(UVC_TRACE_CONTROL,
-				"Control %pUl/%u is already defined.\n",
-				info->entity, info->selector);
-			ret = -EEXIST;
-			goto end;
-		}
-		if (ctrl->index == info->index) {
-			uvc_trace(UVC_TRACE_CONTROL,
-				"Control %pUl/%u would overwrite index %d.\n",
-				info->entity, info->selector, info->index);
-			ret = -EEXIST;
-			goto end;
-		}
-	}
-
-	list_for_each_entry(dev, &uvc_driver.devices, list)
-		uvc_ctrl_add_ctrl(dev, info);
-
-	INIT_LIST_HEAD(&info->mappings);
-	list_add_tail(&info->list, &uvc_driver.controls);
-end:
-	mutex_unlock(&uvc_driver.ctrl_mutex);
-	return ret;
-}
-
-int uvc_ctrl_add_mapping(struct uvc_control_mapping *mapping)
-{
-	struct uvc_control_info *info;
-	struct uvc_control_mapping *map;
-	int ret = -EINVAL;
-
-	if (mapping->get == NULL)
-		mapping->get = uvc_get_le_value;
-	if (mapping->set == NULL)
-		mapping->set = uvc_set_le_value;
-
-	if (mapping->id & ~V4L2_CTRL_ID_MASK) {
-		uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s' with "
-			"invalid control id 0x%08x\n", mapping->name,
-			mapping->id);
-		return -EINVAL;
-	}
-
-	mutex_lock(&uvc_driver.ctrl_mutex);
-	list_for_each_entry(info, &uvc_driver.controls, list) {
-		if (memcmp(info->entity, mapping->entity, 16) ||
-			info->selector != mapping->selector)
-			continue;
-
-		if (info->size * 8 < mapping->size + mapping->offset) {
-			uvc_trace(UVC_TRACE_CONTROL,
-				"Mapping '%s' would overflow control %pUl/%u\n",
-				mapping->name, info->entity, info->selector);
-			ret = -EOVERFLOW;
-			goto end;
-		}
-
-		/* Check if the list contains a mapping matching the new one.
-		 * Bail out if it does.
-		 */
-		list_for_each_entry(map, &info->mappings, list) {
-			if (map->id == mapping->id) {
-				uvc_trace(UVC_TRACE_CONTROL, "Mapping '%s' is "
-					"already defined.\n", mapping->name);
-				ret = -EEXIST;
-				goto end;
-			}
-		}
-
-		mapping->ctrl = info;
-		list_add_tail(&mapping->list, &info->mappings);
-		uvc_trace(UVC_TRACE_CONTROL,
-			"Adding mapping %s to control %pUl/%u.\n",
-			mapping->name, info->entity, info->selector);
-
-		ret = 0;
-		break;
-	}
-end:
-	mutex_unlock(&uvc_driver.ctrl_mutex);
+	mutex_unlock(&chain->ctrl_mutex);
 	return ret;
 }
 
@@ -1497,8 +1489,8 @@
  * are currently the ones that crash the camera or unconditionally return an
  * error when queried.
  */
-static void
-uvc_ctrl_prune_entity(struct uvc_device *dev, struct uvc_entity *entity)
+static void uvc_ctrl_prune_entity(struct uvc_device *dev,
+	struct uvc_entity *entity)
 {
 	struct uvc_ctrl_blacklist {
 		struct usb_device_id id;
@@ -1555,17 +1547,67 @@
 }
 
 /*
+ * Add control information and hardcoded stock control mappings to the given
+ * device.
+ */
+static void uvc_ctrl_init_ctrl(struct uvc_device *dev, struct uvc_control *ctrl)
+{
+	const struct uvc_control_info *info = uvc_ctrls;
+	const struct uvc_control_info *iend = info + ARRAY_SIZE(uvc_ctrls);
+	const struct uvc_control_mapping *mapping = uvc_ctrl_mappings;
+	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));
+		}
+		return;
+	}
+
+	for (; info < iend; ++info) {
+		if (uvc_entity_match_guid(ctrl->entity, info->entity) &&
+		    ctrl->index == info->index) {
+			uvc_ctrl_add_info(dev, ctrl, info);
+			break;
+		 }
+	}
+
+	if (ctrl->info == NULL)
+		return;
+
+	for (; mapping < mend; ++mapping) {
+		if (uvc_entity_match_guid(ctrl->entity, mapping->entity) &&
+		    ctrl->info->selector == mapping->selector)
+			__uvc_ctrl_add_mapping(dev, ctrl, mapping);
+	}
+}
+
+/*
  * Initialize device controls.
  */
 int uvc_ctrl_init_device(struct uvc_device *dev)
 {
-	struct uvc_control_info *info;
-	struct uvc_control *ctrl;
 	struct uvc_entity *entity;
 	unsigned int i;
 
 	/* Walk the entities list and instantiate controls */
 	list_for_each_entry(entity, &dev->entities, list) {
+		struct uvc_control *ctrl;
 		unsigned int bControlSize = 0, ncontrols = 0;
 		__u8 *bmControls = NULL;
 
@@ -1580,20 +1622,22 @@
 			bControlSize = entity->camera.bControlSize;
 		}
 
+		/* Remove bogus/blacklisted controls */
 		uvc_ctrl_prune_entity(dev, entity);
 
+		/* Count supported controls and allocate the controls array */
 		for (i = 0; i < bControlSize; ++i)
 			ncontrols += hweight8(bmControls[i]);
-
 		if (ncontrols == 0)
 			continue;
 
-		entity->controls = kzalloc(ncontrols*sizeof *ctrl, GFP_KERNEL);
+		entity->controls = kzalloc(ncontrols * sizeof(*ctrl),
+					   GFP_KERNEL);
 		if (entity->controls == NULL)
 			return -ENOMEM;
-
 		entity->ncontrols = ncontrols;
 
+		/* Initialize all supported controls */
 		ctrl = entity->controls;
 		for (i = 0; i < bControlSize * 8; ++i) {
 			if (uvc_test_bit(bmControls, i) == 0)
@@ -1601,81 +1645,48 @@
 
 			ctrl->entity = entity;
 			ctrl->index = i;
+
+			uvc_ctrl_init_ctrl(dev, ctrl);
 			ctrl++;
 		}
 	}
 
-	/* Walk the controls info list and associate them with the device
-	 * controls, then add the device to the global device list. This has
-	 * to be done while holding the controls lock, to make sure
-	 * uvc_ctrl_add_info() will not get called in-between.
-	 */
-	mutex_lock(&uvc_driver.ctrl_mutex);
-	list_for_each_entry(info, &uvc_driver.controls, list)
-		uvc_ctrl_add_ctrl(dev, info);
-
-	list_add_tail(&dev->list, &uvc_driver.devices);
-	mutex_unlock(&uvc_driver.ctrl_mutex);
-
 	return 0;
 }
 
 /*
  * Cleanup device controls.
  */
+static void uvc_ctrl_cleanup_mappings(struct uvc_device *dev,
+	struct uvc_control *ctrl)
+{
+	struct uvc_control_mapping *mapping, *nm;
+
+	list_for_each_entry_safe(mapping, nm, &ctrl->info->mappings, list) {
+		list_del(&mapping->list);
+		kfree(mapping->menu_info);
+		kfree(mapping);
+	}
+}
+
 void uvc_ctrl_cleanup_device(struct uvc_device *dev)
 {
 	struct uvc_entity *entity;
 	unsigned int i;
 
-	/* Remove the device from the global devices list */
-	mutex_lock(&uvc_driver.ctrl_mutex);
-	if (dev->list.next != NULL)
-		list_del(&dev->list);
-	mutex_unlock(&uvc_driver.ctrl_mutex);
-
+	/* Free controls and control mappings for all entities. */
 	list_for_each_entry(entity, &dev->entities, list) {
-		for (i = 0; i < entity->ncontrols; ++i)
-			kfree(entity->controls[i].uvc_data);
+		for (i = 0; i < entity->ncontrols; ++i) {
+			struct uvc_control *ctrl = &entity->controls[i];
+
+			if (ctrl->info == NULL)
+				continue;
+
+			uvc_ctrl_cleanup_mappings(dev, ctrl);
+			kfree(ctrl->uvc_data);
+			kfree(ctrl->info);
+		}
 
 		kfree(entity->controls);
 	}
 }
-
-void uvc_ctrl_cleanup(void)
-{
-	struct uvc_control_info *info;
-	struct uvc_control_info *ni;
-	struct uvc_control_mapping *mapping;
-	struct uvc_control_mapping *nm;
-
-	list_for_each_entry_safe(info, ni, &uvc_driver.controls, list) {
-		if (!(info->flags & UVC_CONTROL_EXTENSION))
-			continue;
-
-		list_for_each_entry_safe(mapping, nm, &info->mappings, list) {
-			list_del(&mapping->list);
-			kfree(mapping->menu_info);
-			kfree(mapping);
-		}
-
-		list_del(&info->list);
-		kfree(info);
-	}
-}
-
-void uvc_ctrl_init(void)
-{
-	struct uvc_control_info *ctrl = uvc_ctrls;
-	struct uvc_control_info *cend = ctrl + ARRAY_SIZE(uvc_ctrls);
-	struct uvc_control_mapping *mapping = uvc_ctrl_mappings;
-	struct uvc_control_mapping *mend =
-		mapping + ARRAY_SIZE(uvc_ctrl_mappings);
-
-	for (; ctrl < cend; ++ctrl)
-		uvc_ctrl_add_info(ctrl);
-
-	for (; mapping < mend; ++mapping)
-		uvc_ctrl_add_mapping(mapping);
-}
-