diff options
Diffstat (limited to 'drivers/media/video/uvc/uvc_ctrl.c')
-rw-r--r-- | drivers/media/video/uvc/uvc_ctrl.c | 105 |
1 files changed, 85 insertions, 20 deletions
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index 27a79f087b1..a350fad0db4 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -606,6 +606,26 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .set = uvc_ctrl_set_zoom, }, { + .id = V4L2_CID_PAN_ABSOLUTE, + .name = "Pan (Absolute)", + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL, + .size = 32, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_TILT_ABSOLUTE, + .name = "Tilt (Absolute)", + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL, + .size = 32, + .offset = 32, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { .id = V4L2_CID_PRIVACY, .name = "Privacy", .entity = UVC_GUID_UVC_CAMERA, @@ -623,7 +643,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { static inline __u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id) { - return ctrl->data + id * ctrl->info->size; + return ctrl->uvc_data + id * ctrl->info->size; } static inline int uvc_test_bit(const __u8 *data, int bit) @@ -678,6 +698,14 @@ static void uvc_set_le_value(struct uvc_control_mapping *mapping, int offset = mapping->offset; __u8 mask; + /* According to the v4l2 spec, writing any value to a button control + * should result in the action belonging to the button control being + * triggered. UVC devices however want to see a 1 written -> override + * value. + */ + if (mapping->v4l2_type == V4L2_CTRL_TYPE_BUTTON) + value = -1; + data += offset / 8; offset &= 7; @@ -1265,13 +1293,15 @@ int uvc_ctrl_resume_device(struct uvc_device *dev) * Control and mapping handling */ -static void uvc_ctrl_add_ctrl(struct uvc_device *dev, +static int uvc_ctrl_add_ctrl(struct uvc_device *dev, struct uvc_control_info *info) { struct uvc_entity *entity; struct uvc_control *ctrl = NULL; - int ret, found = 0; + int ret = 0, found = 0; unsigned int i; + u8 *uvc_info; + u8 *uvc_data; list_for_each_entry(entity, &dev->entities, list) { if (!uvc_entity_match_guid(entity, info->entity)) @@ -1290,56 +1320,69 @@ static void uvc_ctrl_add_ctrl(struct uvc_device *dev, } if (!found) - return; + return 0; + + uvc_data = kmalloc(info->size * UVC_CTRL_DATA_LAST + 1, GFP_KERNEL); + if (uvc_data == NULL) + return -ENOMEM; + + 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. */ - __u32 flags; - __le16 size; - __u8 inf; - ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id, - dev->intfnum, info->selector, (__u8 *)&size, 2); + 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); - return; + goto done; } - if (info->size != le16_to_cpu(size)) { + 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); - return; + ret = -EINVAL; + goto done; } ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, - dev->intfnum, info->selector, &inf, 1); + 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); - return; + goto done; } - flags = info->flags; - if (((flags & UVC_CONTROL_GET_CUR) && !(inf & (1 << 0))) || - ((flags & UVC_CONTROL_SET_CUR) && !(inf & (1 << 1)))) { + 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); - return; + ret = -EINVAL; + goto done; } } ctrl->info = info; - ctrl->data = kmalloc(ctrl->info->size * UVC_CTRL_DATA_LAST, GFP_KERNEL); + 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); + +done: + if (ret < 0) + kfree(uvc_data); + + return ret; } /* @@ -1572,12 +1615,34 @@ void uvc_ctrl_cleanup_device(struct uvc_device *dev) list_for_each_entry(entity, &dev->entities, list) { for (i = 0; i < entity->ncontrols; ++i) - kfree(entity->controls[i].data); + kfree(entity->controls[i].uvc_data); 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; |