aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/uvc/uvc_ctrl.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/uvc/uvc_ctrl.c')
-rw-r--r--drivers/media/video/uvc/uvc_ctrl.c109
1 files changed, 87 insertions, 22 deletions
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c
index aa0720af07a0..a350fad0db43 100644
--- a/drivers/media/video/uvc/uvc_ctrl.c
+++ b/drivers/media/video/uvc/uvc_ctrl.c
@@ -122,8 +122,8 @@ static struct uvc_control_info uvc_ctrls[] = {
.selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
.index = 10,
.size = 1,
- .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
- | UVC_CONTROL_RESTORE,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR
+ | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE,
},
{
.entity = UVC_GUID_UVC_PROCESSING,
@@ -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;