V4L/DVB: uvcvideo: Support menu controls in the control mapping API
The UVCIOC_CTRL_MAP ioctl doesn't support menu entries for menu
controls. As the uvc_xu_control_mapping structure has no reserved
fields, this can't be fixed while keeping ABI compatibility.
Modify the UVCIOC_CTRL_MAP ioctl to add menu entries support, and define
UVCIOC_CTRL_MAP_OLD that supports the old ABI without any ability to add
menu controls.
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_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c
index 7c9ab29..485a899 100644
--- a/drivers/media/video/uvc/uvc_v4l2.c
+++ b/drivers/media/video/uvc/uvc_v4l2.c
@@ -29,6 +29,71 @@
#include "uvcvideo.h"
/* ------------------------------------------------------------------------
+ * UVC ioctls
+ */
+static int uvc_ioctl_ctrl_map(struct uvc_xu_control_mapping *xmap, int old)
+{
+ struct uvc_control_mapping *map;
+ unsigned int size;
+ int ret;
+
+ map = kzalloc(sizeof *map, GFP_KERNEL);
+ if (map == NULL)
+ return -ENOMEM;
+
+ map->id = xmap->id;
+ memcpy(map->name, xmap->name, sizeof map->name);
+ memcpy(map->entity, xmap->entity, sizeof map->entity);
+ map->selector = xmap->selector;
+ map->size = xmap->size;
+ map->offset = xmap->offset;
+ map->v4l2_type = xmap->v4l2_type;
+ map->data_type = xmap->data_type;
+
+ switch (xmap->v4l2_type) {
+ case V4L2_CTRL_TYPE_INTEGER:
+ case V4L2_CTRL_TYPE_BOOLEAN:
+ case V4L2_CTRL_TYPE_BUTTON:
+ break;
+
+ case V4L2_CTRL_TYPE_MENU:
+ if (old) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ size = xmap->menu_count * sizeof(*map->menu_info);
+ map->menu_info = kmalloc(size, GFP_KERNEL);
+ if (map->menu_info == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ if (copy_from_user(map->menu_info, xmap->menu_info, size)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ map->menu_count = xmap->menu_count;
+ break;
+
+ default:
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = uvc_ctrl_add_mapping(map);
+
+done:
+ if (ret < 0) {
+ kfree(map->menu_info);
+ kfree(map);
+ }
+
+ return ret;
+}
+
+/* ------------------------------------------------------------------------
* V4L2 interface
*/
@@ -974,7 +1039,8 @@
info->flags = xinfo->flags;
info->flags |= UVC_CONTROL_GET_MIN | UVC_CONTROL_GET_MAX |
- UVC_CONTROL_GET_RES | UVC_CONTROL_GET_DEF;
+ UVC_CONTROL_GET_RES | UVC_CONTROL_GET_DEF |
+ UVC_CONTROL_EXTENSION;
ret = uvc_ctrl_add_info(info);
if (ret < 0)
@@ -982,32 +1048,12 @@
break;
}
+ case UVCIOC_CTRL_MAP_OLD:
case UVCIOC_CTRL_MAP:
- {
- struct uvc_xu_control_mapping *xmap = arg;
- struct uvc_control_mapping *map;
-
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- map = kzalloc(sizeof *map, GFP_KERNEL);
- if (map == NULL)
- return -ENOMEM;
-
- map->id = xmap->id;
- memcpy(map->name, xmap->name, sizeof map->name);
- memcpy(map->entity, xmap->entity, sizeof map->entity);
- map->selector = xmap->selector;
- map->size = xmap->size;
- map->offset = xmap->offset;
- map->v4l2_type = xmap->v4l2_type;
- map->data_type = xmap->data_type;
-
- ret = uvc_ctrl_add_mapping(map);
- if (ret < 0)
- kfree(map);
- break;
- }
+ return uvc_ioctl_ctrl_map(arg, cmd == UVCIOC_CTRL_MAP_OLD);
case UVCIOC_CTRL_GET:
return uvc_xu_ctrl_query(chain, arg, 0);