Commit ba2fa996 authored by Laurent Pinchart's avatar Laurent Pinchart Committed by Mauro Carvalho Chehab

[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: default avatarMartin Rubli <martin_rubli@logitech.com>
Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 650b95fe
...@@ -1294,201 +1294,193 @@ int uvc_ctrl_resume_device(struct uvc_device *dev) ...@@ -1294,201 +1294,193 @@ int uvc_ctrl_resume_device(struct uvc_device *dev)
* Control and mapping handling * 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; u8 *data;
struct uvc_control *ctrl = NULL; int ret;
int ret = 0, found = 0;
unsigned int i;
u8 *uvc_info;
u8 *uvc_data;
list_for_each_entry(entity, &dev->entities, list) { data = kmalloc(2, GFP_KERNEL);
if (!uvc_entity_match_guid(entity, info->entity)) if (data == NULL)
continue; return -ENOMEM;
for (i = 0; i < entity->ncontrols; ++i) { memcpy(info->entity, ctrl->entity->extension.guidExtensionCode,
ctrl = &entity->controls[i]; sizeof(info->entity));
if (ctrl->index == info->index) { info->index = ctrl->index;
found = 1; info->selector = ctrl->index + 1;
break;
}
}
if (found) /* Query and verify the control length (GET_LEN) */
break; 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) info->size = le16_to_cpup((__le16 *)data);
return 0;
uvc_data = kmalloc(info->size * UVC_CTRL_DATA_LAST + 1, GFP_KERNEL); /* Query the control information (GET_INFO) */
if (uvc_data == NULL) ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, dev->intfnum,
return -ENOMEM; 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) { uvc_trace(UVC_TRACE_CONTROL, "XU control %pUl/%u queried: len %u, "
/* Check if the device control information and length match "flags { get %u set %u auto %u }.\n",
* the user supplied information. info->entity, info->selector, info->size,
*/ (info->flags & UVC_CONTROL_GET_CUR) ? 1 : 0,
ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id, (info->flags & UVC_CONTROL_SET_CUR) ? 1 : 0,
dev->intfnum, info->selector, uvc_data, 2); (info->flags & UVC_CONTROL_AUTO_UPDATE) ? 1 : 0);
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)) { done:
uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u size " kfree(data);
"doesn't match user supplied value.\n", return ret;
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); * Add control information to a given control.
if (ret < 0) { */
uvc_trace(UVC_TRACE_CONTROL, static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl,
"GET_INFO failed on control %pUl/%u (%d).\n", const struct uvc_control_info *info)
info->entity, info->selector, ret); {
goto done; int ret = 0;
}
if (((info->flags & UVC_CONTROL_GET_CUR) && /* Clone the control info struct for this device's instance */
!(*uvc_info & UVC_CONTROL_CAP_GET)) || ctrl->info = kmemdup(info, sizeof(*info), GFP_KERNEL);
((info->flags & UVC_CONTROL_SET_CUR) && if (ctrl->info == NULL) {
!(*uvc_info & UVC_CONTROL_CAP_SET))) { ret = -ENOMEM;
uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u flags " goto done;
"don't match supported operations.\n", }
info->entity, info->selector); INIT_LIST_HEAD(&ctrl->info->mappings);
ret = -EINVAL;
goto done; /* 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 " uvc_trace(UVC_TRACE_CONTROL, "Added control %pUl/%u to device %s "
"entity %u\n", ctrl->info->entity, ctrl->info->selector, "entity %u\n", ctrl->info->entity, ctrl->info->selector,
dev->udev->devpath, entity->id); dev->udev->devpath, ctrl->entity->id);
done: done:
if (ret < 0) if (ret < 0) {
kfree(uvc_data); kfree(ctrl->uvc_data);
kfree(ctrl->info);
}
return ret; return ret;
} }
/* /*
* Add an item to the UVC control information list, and instantiate a control * Add a control mapping to a given control.
* structure for each device that supports the 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_control_mapping *map;
struct uvc_device *dev; unsigned int size;
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. /* Most mappings come from static kernel data and need to be duplicated.
* Bail out if it does. * Mappings that come from userspace will be unnecessarily duplicated,
* this could be optimized.
*/ */
list_for_each_entry(ctrl, &uvc_driver.controls, list) { map = kmemdup(mapping, sizeof(*mapping), GFP_KERNEL);
if (memcmp(ctrl->entity, info->entity, 16)) if (map == NULL)
continue; return -ENOMEM;
if (ctrl->selector == info->selector) { size = sizeof(*mapping->menu_info) * mapping->menu_count;
uvc_trace(UVC_TRACE_CONTROL, map->menu_info = kmemdup(mapping->menu_info, size, GFP_KERNEL);
"Control %pUl/%u is already defined.\n", if (map->menu_info == NULL) {
info->entity, info->selector); kfree(map);
ret = -EEXIST; return -ENOMEM;
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) if (map->get == NULL)
uvc_ctrl_add_ctrl(dev, info); map->get = uvc_get_le_value;
if (map->set == NULL)
map->set = uvc_set_le_value;
INIT_LIST_HEAD(&info->mappings); map->ctrl = ctrl->info;
list_add_tail(&info->list, &uvc_driver.controls); list_add_tail(&map->list, &ctrl->info->mappings);
end: uvc_trace(UVC_TRACE_CONTROL,
mutex_unlock(&uvc_driver.ctrl_mutex); "Adding mapping '%s' to control %pUl/%u.\n",
return ret; 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; struct uvc_control_mapping *map;
int ret = -EINVAL; struct uvc_entity *entity;
struct uvc_control *ctrl;
if (mapping->get == NULL) int found = 0;
mapping->get = uvc_get_le_value;
if (mapping->set == NULL)
mapping->set = uvc_set_le_value;
if (mapping->id & ~V4L2_CTRL_ID_MASK) { if (mapping->id & ~V4L2_CTRL_ID_MASK) {
uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s' with " uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', control "
"invalid control id 0x%08x\n", mapping->name, "control id 0x%08x is invalid.\n", mapping->name,
mapping->id); mapping->id);
return -EINVAL; return -EINVAL;
} }
mutex_lock(&uvc_driver.ctrl_mutex); /* Search for the matching (GUID/CS) control in the given device */
list_for_each_entry(info, &uvc_driver.controls, list) { list_for_each_entry(entity, &dev->entities, list) {
if (memcmp(info->entity, mapping->entity, 16) || unsigned int i;
info->selector != mapping->selector)
continue;
if (info->size * 8 < mapping->size + mapping->offset) { if (!uvc_entity_match_guid(entity, mapping->entity))
uvc_trace(UVC_TRACE_CONTROL, continue;
"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. for (i = 0; i < entity->ncontrols; ++i) {
* Bail out if it does. ctrl = &entity->controls[i];
*/ if (ctrl->info != NULL &&
list_for_each_entry(map, &info->mappings, list) { ctrl->info->selector == mapping->selector) {
if (map->id == mapping->id) { found = 1;
uvc_trace(UVC_TRACE_CONTROL, "Mapping '%s' is " break;
"already defined.\n", mapping->name);
ret = -EEXIST;
goto end;
} }
} }
mapping->ctrl = info; if (found)
list_add_tail(&mapping->list, &info->mappings); break;
uvc_trace(UVC_TRACE_CONTROL, }
"Adding mapping %s to control %pUl/%u.\n", if (!found)
mapping->name, info->entity, info->selector); return -ENOENT;
ret = 0; if (mutex_lock_interruptible(&chain->ctrl_mutex))
break; 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; return ret;
} }
...@@ -1497,8 +1489,8 @@ int uvc_ctrl_add_mapping(struct uvc_control_mapping *mapping) ...@@ -1497,8 +1489,8 @@ int uvc_ctrl_add_mapping(struct uvc_control_mapping *mapping)
* are currently the ones that crash the camera or unconditionally return an * are currently the ones that crash the camera or unconditionally return an
* error when queried. * error when queried.
*/ */
static void static void uvc_ctrl_prune_entity(struct uvc_device *dev,
uvc_ctrl_prune_entity(struct uvc_device *dev, struct uvc_entity *entity) struct uvc_entity *entity)
{ {
struct uvc_ctrl_blacklist { struct uvc_ctrl_blacklist {
struct usb_device_id id; struct usb_device_id id;
...@@ -1554,18 +1546,68 @@ uvc_ctrl_prune_entity(struct uvc_device *dev, struct uvc_entity *entity) ...@@ -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. * Initialize device controls.
*/ */
int uvc_ctrl_init_device(struct uvc_device *dev) int uvc_ctrl_init_device(struct uvc_device *dev)
{ {
struct uvc_control_info *info;
struct uvc_control *ctrl;
struct uvc_entity *entity; struct uvc_entity *entity;
unsigned int i; unsigned int i;
/* Walk the entities list and instantiate controls */ /* Walk the entities list and instantiate controls */
list_for_each_entry(entity, &dev->entities, list) { list_for_each_entry(entity, &dev->entities, list) {
struct uvc_control *ctrl;
unsigned int bControlSize = 0, ncontrols = 0; unsigned int bControlSize = 0, ncontrols = 0;
__u8 *bmControls = NULL; __u8 *bmControls = NULL;
...@@ -1580,20 +1622,22 @@ int uvc_ctrl_init_device(struct uvc_device *dev) ...@@ -1580,20 +1622,22 @@ int uvc_ctrl_init_device(struct uvc_device *dev)
bControlSize = entity->camera.bControlSize; bControlSize = entity->camera.bControlSize;
} }
/* Remove bogus/blacklisted controls */
uvc_ctrl_prune_entity(dev, entity); uvc_ctrl_prune_entity(dev, entity);
/* Count supported controls and allocate the controls array */
for (i = 0; i < bControlSize; ++i) for (i = 0; i < bControlSize; ++i)
ncontrols += hweight8(bmControls[i]); ncontrols += hweight8(bmControls[i]);
if (ncontrols == 0) if (ncontrols == 0)
continue; continue;
entity->controls = kzalloc(ncontrols*sizeof *ctrl, GFP_KERNEL); entity->controls = kzalloc(ncontrols * sizeof(*ctrl),
GFP_KERNEL);
if (entity->controls == NULL) if (entity->controls == NULL)
return -ENOMEM; return -ENOMEM;
entity->ncontrols = ncontrols; entity->ncontrols = ncontrols;
/* Initialize all supported controls */
ctrl = entity->controls; ctrl = entity->controls;
for (i = 0; i < bControlSize * 8; ++i) { for (i = 0; i < bControlSize * 8; ++i) {
if (uvc_test_bit(bmControls, i) == 0) if (uvc_test_bit(bmControls, i) == 0)
...@@ -1601,81 +1645,48 @@ int uvc_ctrl_init_device(struct uvc_device *dev) ...@@ -1601,81 +1645,48 @@ int uvc_ctrl_init_device(struct uvc_device *dev)
ctrl->entity = entity; ctrl->entity = entity;
ctrl->index = i; ctrl->index = i;
uvc_ctrl_init_ctrl(dev, ctrl);
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; return 0;
} }
/* /*
* Cleanup device controls. * 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; struct uvc_control_mapping *mapping, *nm;
unsigned int i;
/* Remove the device from the global devices list */ list_for_each_entry_safe(mapping, nm, &ctrl->info->mappings, list) {
mutex_lock(&uvc_driver.ctrl_mutex); list_del(&mapping->list);
if (dev->list.next != NULL) kfree(mapping->menu_info);
list_del(&dev->list); kfree(mapping);
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);
} }
} }
void uvc_ctrl_cleanup(void) void uvc_ctrl_cleanup_device(struct uvc_device *dev)
{ {
struct uvc_control_info *info; struct uvc_entity *entity;
struct uvc_control_info *ni; unsigned int i;
struct uvc_control_mapping *mapping;
struct uvc_control_mapping *nm;
list_for_each_entry_safe(info, ni, &uvc_driver.controls, list) { /* Free controls and control mappings for all entities. */
if (!(info->flags & UVC_CONTROL_EXTENSION)) list_for_each_entry(entity, &dev->entities, list) {
continue; 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) { uvc_ctrl_cleanup_mappings(dev, ctrl);
list_del(&mapping->list); kfree(ctrl->uvc_data);
kfree(mapping->menu_info); kfree(ctrl->info);
kfree(mapping);
} }
list_del(&info->list); kfree(entity->controls);
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);
}
...@@ -41,9 +41,6 @@ ...@@ -41,9 +41,6 @@
#define DRIVER_AUTHOR "Laurent Pinchart " \ #define DRIVER_AUTHOR "Laurent Pinchart " \
"<laurent.pinchart@ideasonboard.com>" "<laurent.pinchart@ideasonboard.com>"
#define DRIVER_DESC "USB Video Class driver" #define DRIVER_DESC "USB Video Class driver"
#ifndef DRIVER_VERSION
#define DRIVER_VERSION "v0.1.0"
#endif
unsigned int uvc_clock_param = CLOCK_MONOTONIC; unsigned int uvc_clock_param = CLOCK_MONOTONIC;
unsigned int uvc_no_drop_param; unsigned int uvc_no_drop_param;
...@@ -2289,12 +2286,6 @@ static int __init uvc_init(void) ...@@ -2289,12 +2286,6 @@ static int __init uvc_init(void)
{ {
int result; int result;
INIT_LIST_HEAD(&uvc_driver.devices);
INIT_LIST_HEAD(&uvc_driver.controls);
mutex_init(&uvc_driver.ctrl_mutex);
uvc_ctrl_init();
result = usb_register(&uvc_driver.driver); result = usb_register(&uvc_driver.driver);
if (result == 0) if (result == 0)
printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")\n"); printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")\n");
...@@ -2304,7 +2295,6 @@ static int __init uvc_init(void) ...@@ -2304,7 +2295,6 @@ static int __init uvc_init(void)
static void __exit uvc_cleanup(void) static void __exit uvc_cleanup(void)
{ {
usb_deregister(&uvc_driver.driver); usb_deregister(&uvc_driver.driver);
uvc_ctrl_cleanup();
} }
module_init(uvc_init); module_init(uvc_init);
......
...@@ -31,7 +31,8 @@ ...@@ -31,7 +31,8 @@
/* ------------------------------------------------------------------------ /* ------------------------------------------------------------------------
* UVC ioctls * UVC ioctls
*/ */
static int uvc_ioctl_ctrl_map(struct uvc_xu_control_mapping *xmap, int old) static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain,
struct uvc_xu_control_mapping *xmap, int old)
{ {
struct uvc_control_mapping *map; struct uvc_control_mapping *map;
unsigned int size; unsigned int size;
...@@ -58,6 +59,8 @@ static int uvc_ioctl_ctrl_map(struct uvc_xu_control_mapping *xmap, int old) ...@@ -58,6 +59,8 @@ static int uvc_ioctl_ctrl_map(struct uvc_xu_control_mapping *xmap, int old)
case V4L2_CTRL_TYPE_MENU: case V4L2_CTRL_TYPE_MENU:
if (old) { if (old) {
uvc_trace(UVC_TRACE_CONTROL, "V4L2_CTRL_TYPE_MENU not "
"supported for UVCIOC_CTRL_MAP_OLD.\n");
ret = -EINVAL; ret = -EINVAL;
goto done; goto done;
} }
...@@ -78,17 +81,17 @@ static int uvc_ioctl_ctrl_map(struct uvc_xu_control_mapping *xmap, int old) ...@@ -78,17 +81,17 @@ static int uvc_ioctl_ctrl_map(struct uvc_xu_control_mapping *xmap, int old)
break; break;
default: default:
uvc_trace(UVC_TRACE_CONTROL, "Unsupported V4L2 control type "
"%u.\n", xmap->v4l2_type);
ret = -EINVAL; ret = -EINVAL;
goto done; goto done;
} }
ret = uvc_ctrl_add_mapping(map); ret = uvc_ctrl_add_mapping(chain, map);
done: done:
if (ret < 0) { kfree(map->menu_info);
kfree(map->menu_info); kfree(map);
kfree(map);
}
return ret; return ret;
} }
...@@ -1021,42 +1024,19 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ...@@ -1021,42 +1024,19 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
/* Dynamic controls. */ /* Dynamic controls. */
case UVCIOC_CTRL_ADD: case UVCIOC_CTRL_ADD:
{ /* Legacy ioctl, kept for API compatibility reasons */
struct uvc_xu_control_info *xinfo = arg;
struct uvc_control_info *info;
if (!capable(CAP_SYS_ADMIN)) if (!capable(CAP_SYS_ADMIN))
return -EPERM; return -EPERM;
if (xinfo->size == 0) return -EEXIST;
return -EINVAL;
info = kzalloc(sizeof *info, GFP_KERNEL);
if (info == NULL)
return -ENOMEM;
memcpy(info->entity, xinfo->entity, sizeof info->entity);
info->index = xinfo->index;
info->selector = xinfo->selector;
info->size = xinfo->size;
info->flags = xinfo->flags;
info->flags |= UVC_CONTROL_GET_MIN | UVC_CONTROL_GET_MAX |
UVC_CONTROL_GET_RES | UVC_CONTROL_GET_DEF |
UVC_CONTROL_EXTENSION;
ret = uvc_ctrl_add_info(info);
if (ret < 0)
kfree(info);
break;
}
case UVCIOC_CTRL_MAP_OLD: case UVCIOC_CTRL_MAP_OLD:
case UVCIOC_CTRL_MAP: case UVCIOC_CTRL_MAP:
if (!capable(CAP_SYS_ADMIN)) if (!capable(CAP_SYS_ADMIN))
return -EPERM; return -EPERM;
return uvc_ioctl_ctrl_map(arg, cmd == UVCIOC_CTRL_MAP_OLD); return uvc_ioctl_ctrl_map(chain, arg,
cmd == UVCIOC_CTRL_MAP_OLD);
case UVCIOC_CTRL_GET: case UVCIOC_CTRL_GET:
return uvc_xu_ctrl_query(chain, arg, 0); return uvc_xu_ctrl_query(chain, arg, 0);
......
...@@ -27,8 +27,6 @@ ...@@ -27,8 +27,6 @@
#define UVC_CONTROL_RESTORE (1 << 6) #define UVC_CONTROL_RESTORE (1 << 6)
/* Control can be updated by the camera. */ /* Control can be updated by the camera. */
#define UVC_CONTROL_AUTO_UPDATE (1 << 7) #define UVC_CONTROL_AUTO_UPDATE (1 << 7)
/* Control is an extension unit control. */
#define UVC_CONTROL_EXTENSION (1 << 8)
#define UVC_CONTROL_GET_RANGE (UVC_CONTROL_GET_CUR | UVC_CONTROL_GET_MIN | \ #define UVC_CONTROL_GET_RANGE (UVC_CONTROL_GET_CUR | UVC_CONTROL_GET_MIN | \
UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES | \ UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES | \
...@@ -159,7 +157,8 @@ struct uvc_xu_control { ...@@ -159,7 +157,8 @@ struct uvc_xu_control {
* Driver specific constants. * Driver specific constants.
*/ */
#define DRIVER_VERSION_NUMBER KERNEL_VERSION(0, 1, 0) #define DRIVER_VERSION_NUMBER KERNEL_VERSION(1, 0, 0)
#define DRIVER_VERSION "v1.0.0"
/* Number of isochronous URBs. */ /* Number of isochronous URBs. */
#define UVC_URBS 5 #define UVC_URBS 5
...@@ -198,11 +197,10 @@ struct uvc_device; ...@@ -198,11 +197,10 @@ struct uvc_device;
* structures to maximize cache efficiency. * structures to maximize cache efficiency.
*/ */
struct uvc_control_info { struct uvc_control_info {
struct list_head list;
struct list_head mappings; struct list_head mappings;
__u8 entity[16]; __u8 entity[16];
__u8 index; __u8 index; /* Bit index in bmControls */
__u8 selector; __u8 selector;
__u16 size; __u16 size;
...@@ -245,7 +243,6 @@ struct uvc_control { ...@@ -245,7 +243,6 @@ struct uvc_control {
cached : 1; cached : 1;
__u8 *uvc_data; __u8 *uvc_data;
__u8 *uvc_info;
}; };
struct uvc_format_desc { struct uvc_format_desc {
...@@ -474,7 +471,6 @@ struct uvc_device { ...@@ -474,7 +471,6 @@ struct uvc_device {
char name[32]; char name[32];
enum uvc_device_state state; enum uvc_device_state state;
struct list_head list;
atomic_t users; atomic_t users;
/* Video control interface */ /* Video control interface */
...@@ -509,11 +505,6 @@ struct uvc_fh { ...@@ -509,11 +505,6 @@ struct uvc_fh {
struct uvc_driver { struct uvc_driver {
struct usb_driver driver; struct usb_driver driver;
struct list_head devices; /* struct uvc_device list */
struct list_head controls; /* struct uvc_control_info list */
struct mutex ctrl_mutex; /* protects controls and devices
lists */
}; };
/* ------------------------------------------------------------------------ /* ------------------------------------------------------------------------
...@@ -615,13 +606,11 @@ extern struct uvc_control *uvc_find_control(struct uvc_video_chain *chain, ...@@ -615,13 +606,11 @@ extern struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
extern int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, extern int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
struct v4l2_queryctrl *v4l2_ctrl); struct v4l2_queryctrl *v4l2_ctrl);
extern int uvc_ctrl_add_info(struct uvc_control_info *info); extern int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
extern int uvc_ctrl_add_mapping(struct uvc_control_mapping *mapping); const struct uvc_control_mapping *mapping);
extern int uvc_ctrl_init_device(struct uvc_device *dev); extern int uvc_ctrl_init_device(struct uvc_device *dev);
extern void uvc_ctrl_cleanup_device(struct uvc_device *dev); extern void uvc_ctrl_cleanup_device(struct uvc_device *dev);
extern int uvc_ctrl_resume_device(struct uvc_device *dev); extern int uvc_ctrl_resume_device(struct uvc_device *dev);
extern void uvc_ctrl_init(void);
extern void uvc_ctrl_cleanup(void);
extern int uvc_ctrl_begin(struct uvc_video_chain *chain); extern int uvc_ctrl_begin(struct uvc_video_chain *chain);
extern int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback); extern int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment