|
@@ -1294,201 +1294,193 @@ int uvc_ctrl_resume_device(struct uvc_device *dev)
|
|
|
* 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;
|
|
|
|
|
|
- list_for_each_entry(entity, &dev->entities, list) {
|
|
|
- if (!uvc_entity_match_guid(entity, info->entity))
|
|
|
- continue;
|
|
|
+ data = kmalloc(2, GFP_KERNEL);
|
|
|
+ if (data == NULL)
|
|
|
+ return -ENOMEM;
|
|
|
|
|
|
- for (i = 0; i < entity->ncontrols; ++i) {
|
|
|
- ctrl = &entity->controls[i];
|
|
|
- if (ctrl->index == info->index) {
|
|
|
- found = 1;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
+ memcpy(info->entity, ctrl->entity->extension.guidExtensionCode,
|
|
|
+ sizeof(info->entity));
|
|
|
+ info->index = ctrl->index;
|
|
|
+ info->selector = ctrl->index + 1;
|
|
|
|
|
|
- if (found)
|
|
|
- break;
|
|
|
+ /* 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;
|
|
|
}
|
|
|
|
|
|
- if (!found)
|
|
|
- return 0;
|
|
|
+ info->size = le16_to_cpup((__le16 *)data);
|
|
|
|
|
|
- uvc_data = kmalloc(info->size * UVC_CTRL_DATA_LAST + 1, GFP_KERNEL);
|
|
|
- if (uvc_data == NULL)
|
|
|
- return -ENOMEM;
|
|
|
+ /* 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;
|
|
|
+ }
|
|
|
|
|
|
- uvc_info = uvc_data + info->size * UVC_CTRL_DATA_LAST;
|
|
|
+ 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);
|
|
|
|
|
|
- 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;
|
|
|
- }
|
|
|
+ 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);
|
|
|
|
|
|
- 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;
|
|
|
- }
|
|
|
+done:
|
|
|
+ kfree(data);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
|
|
|
- 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;
|
|
|
- }
|
|
|
+/*
|
|
|
+ * 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;
|
|
|
|
|
|
- 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;
|
|
|
- goto done;
|
|
|
- }
|
|
|
+ /* 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;
|
|
|
}
|
|
|
-
|
|
|
- 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);
|
|
|
+ dev->udev->devpath, ctrl->entity->id);
|
|
|
|
|
|
done:
|
|
|
- if (ret < 0)
|
|
|
- kfree(uvc_data);
|
|
|
-
|
|
|
+ if (ret < 0) {
|
|
|
+ kfree(ctrl->uvc_data);
|
|
|
+ kfree(ctrl->info);
|
|
|
+ }
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * Add an item to the UVC control information list, and instantiate a control
|
|
|
- * structure for each device that supports the control.
|
|
|
+ * Add a control mapping to a given control.
|
|
|
*/
|
|
|
-int uvc_ctrl_add_info(struct uvc_control_info *info)
|
|
|
+static int __uvc_ctrl_add_mapping(struct uvc_device *dev,
|
|
|
+ struct uvc_control *ctrl, const struct uvc_control_mapping *mapping)
|
|
|
{
|
|
|
- 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);
|
|
|
+ struct uvc_control_mapping *map;
|
|
|
+ unsigned int size;
|
|
|
|
|
|
- /* First check if the list contains a control matching the new one.
|
|
|
- * Bail out if it does.
|
|
|
+ /* 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.
|
|
|
*/
|
|
|
- list_for_each_entry(ctrl, &uvc_driver.controls, list) {
|
|
|
- if (memcmp(ctrl->entity, info->entity, 16))
|
|
|
- continue;
|
|
|
+ map = kmemdup(mapping, sizeof(*mapping), GFP_KERNEL);
|
|
|
+ if (map == NULL)
|
|
|
+ return -ENOMEM;
|
|
|
|
|
|
- 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;
|
|
|
- }
|
|
|
+ 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;
|
|
|
}
|
|
|
|
|
|
- list_for_each_entry(dev, &uvc_driver.devices, list)
|
|
|
- uvc_ctrl_add_ctrl(dev, info);
|
|
|
+ if (map->get == NULL)
|
|
|
+ map->get = uvc_get_le_value;
|
|
|
+ if (map->set == NULL)
|
|
|
+ map->set = uvc_set_le_value;
|
|
|
|
|
|
- INIT_LIST_HEAD(&info->mappings);
|
|
|
- list_add_tail(&info->list, &uvc_driver.controls);
|
|
|
-end:
|
|
|
- mutex_unlock(&uvc_driver.ctrl_mutex);
|
|
|
- return ret;
|
|
|
+ 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_control_mapping *mapping)
|
|
|
+int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
|
|
|
+ const struct uvc_control_mapping *mapping)
|
|
|
{
|
|
|
- struct uvc_control_info *info;
|
|
|
+ struct uvc_device *dev = chain->dev;
|
|
|
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;
|
|
|
+ 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' with "
|
|
|
- "invalid control id 0x%08x\n", mapping->name,
|
|
|
+ uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', control "
|
|
|
+ "control id 0x%08x is invalid.\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;
|
|
|
+ /* Search for the matching (GUID/CS) control in the given device */
|
|
|
+ list_for_each_entry(entity, &dev->entities, list) {
|
|
|
+ unsigned int i;
|
|
|
|
|
|
- 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;
|
|
|
- }
|
|
|
+ if (!uvc_entity_match_guid(entity, mapping->entity))
|
|
|
+ continue;
|
|
|
|
|
|
- /* 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;
|
|
|
+ for (i = 0; i < entity->ncontrols; ++i) {
|
|
|
+ ctrl = &entity->controls[i];
|
|
|
+ if (ctrl->info != NULL &&
|
|
|
+ ctrl->info->selector == mapping->selector) {
|
|
|
+ found = 1;
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- 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);
|
|
|
+ if (found)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (!found)
|
|
|
+ return -ENOENT;
|
|
|
|
|
|
- ret = 0;
|
|
|
- break;
|
|
|
+ if (mutex_lock_interruptible(&chain->ctrl_mutex))
|
|
|
+ return -ERESTARTSYS;
|
|
|
+
|
|
|
+ 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;
|
|
|
+ }
|
|
|
}
|
|
|
-end:
|
|
|
- mutex_unlock(&uvc_driver.ctrl_mutex);
|
|
|
+
|
|
|
+ ret = __uvc_ctrl_add_mapping(dev, ctrl, mapping);
|
|
|
+done:
|
|
|
+ mutex_unlock(&chain->ctrl_mutex);
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
@@ -1497,8 +1489,8 @@ end:
|
|
|
* 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;
|
|
@@ -1554,18 +1546,68 @@ uvc_ctrl_prune_entity(struct uvc_device *dev, struct uvc_entity *entity)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * 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 @@ int uvc_ctrl_init_device(struct uvc_device *dev)
|
|
|
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 @@ int uvc_ctrl_init_device(struct uvc_device *dev)
|
|
|
|
|
|
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.
|
|
|
*/
|
|
|
-void uvc_ctrl_cleanup_device(struct uvc_device *dev)
|
|
|
+static void uvc_ctrl_cleanup_mappings(struct uvc_device *dev,
|
|
|
+ struct uvc_control *ctrl)
|
|
|
{
|
|
|
- struct uvc_entity *entity;
|
|
|
- unsigned int i;
|
|
|
+ struct uvc_control_mapping *mapping, *nm;
|
|
|
|
|
|
- /* 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);
|
|
|
-
|
|
|
- list_for_each_entry(entity, &dev->entities, list) {
|
|
|
- for (i = 0; i < entity->ncontrols; ++i)
|
|
|
- kfree(entity->controls[i].uvc_data);
|
|
|
-
|
|
|
- kfree(entity->controls);
|
|
|
+ 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(void)
|
|
|
+void uvc_ctrl_cleanup_device(struct uvc_device *dev)
|
|
|
{
|
|
|
- struct uvc_control_info *info;
|
|
|
- struct uvc_control_info *ni;
|
|
|
- struct uvc_control_mapping *mapping;
|
|
|
- struct uvc_control_mapping *nm;
|
|
|
+ struct uvc_entity *entity;
|
|
|
+ unsigned int i;
|
|
|
|
|
|
- list_for_each_entry_safe(info, ni, &uvc_driver.controls, list) {
|
|
|
- if (!(info->flags & UVC_CONTROL_EXTENSION))
|
|
|
- continue;
|
|
|
+ /* Free controls and control mappings for all entities. */
|
|
|
+ list_for_each_entry(entity, &dev->entities, list) {
|
|
|
+ for (i = 0; i < entity->ncontrols; ++i) {
|
|
|
+ struct uvc_control *ctrl = &entity->controls[i];
|
|
|
+
|
|
|
+ if (ctrl->info == NULL)
|
|
|
+ continue;
|
|
|
|
|
|
- list_for_each_entry_safe(mapping, nm, &info->mappings, list) {
|
|
|
- list_del(&mapping->list);
|
|
|
- kfree(mapping->menu_info);
|
|
|
- kfree(mapping);
|
|
|
+ uvc_ctrl_cleanup_mappings(dev, ctrl);
|
|
|
+ kfree(ctrl->uvc_data);
|
|
|
+ kfree(ctrl->info);
|
|
|
}
|
|
|
|
|
|
- list_del(&info->list);
|
|
|
- kfree(info);
|
|
|
+ kfree(entity->controls);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
-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);
|
|
|
-}
|
|
|
-
|