V4L/DVB: uvcvideo: Support menu controls in the control mapping API
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Thu, 18 Feb 2010 19:38:52 +0000 (16:38 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Mon, 2 Aug 2010 18:33:39 +0000 (15:33 -0300)
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>
drivers/media/video/uvc/uvc_ctrl.c
drivers/media/video/uvc/uvc_driver.c
drivers/media/video/uvc/uvc_v4l2.c
drivers/media/video/uvc/uvcvideo.h

index 4e6d484911fe30eb1319a157d284d8b056c20d46..bd72100a21ddc33ab7dcccf79ff4ec0a60ebdae2 100644 (file)
@@ -1606,6 +1606,28 @@ void uvc_ctrl_cleanup_device(struct uvc_device *dev)
        }
 }
 
+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;
index 45aac10418621b2eeab700b9f3dd8d11df92def8..c5258b30777fdc16e0f86c860542e1c13e577e43 100644 (file)
@@ -2260,6 +2260,7 @@ static int __init uvc_init(void)
 static void __exit uvc_cleanup(void)
 {
        usb_deregister(&uvc_driver.driver);
+       uvc_ctrl_cleanup();
 }
 
 module_init(uvc_init);
index 7c9ab2933496ea4515f6b0544c561b7769f87052..485a89912cef5c79c4e262b1de8cd79be0d57766 100644 (file)
 
 #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 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
                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 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
                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);
index d1f88406a5e7cf784b108d6c0bc1e3584871d742..14f77e42fd4500234606651a3482c88ea435f89c 100644 (file)
@@ -27,6 +27,8 @@
 #define UVC_CONTROL_RESTORE    (1 << 6)
 /* Control can be updated by the camera. */
 #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 | \
                                 UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES | \
@@ -40,6 +42,15 @@ struct uvc_xu_control_info {
        __u32 flags;
 };
 
+struct uvc_menu_info {
+       __u32 value;
+       __u8 name[32];
+};
+
+struct uvc_xu_control_mapping_old {
+       __u8 reserved[64];
+};
+
 struct uvc_xu_control_mapping {
        __u32 id;
        __u8 name[32];
@@ -50,6 +61,11 @@ struct uvc_xu_control_mapping {
        __u8 offset;
        enum v4l2_ctrl_type v4l2_type;
        __u32 data_type;
+
+       struct uvc_menu_info __user *menu_info;
+       __u32 menu_count;
+
+       __u32 reserved[4];
 };
 
 struct uvc_xu_control {
@@ -60,6 +76,7 @@ struct uvc_xu_control {
 };
 
 #define UVCIOC_CTRL_ADD                _IOW('U', 1, struct uvc_xu_control_info)
+#define UVCIOC_CTRL_MAP_OLD    _IOWR('U', 2, struct uvc_xu_control_mapping_old)
 #define UVCIOC_CTRL_MAP                _IOWR('U', 2, struct uvc_xu_control_mapping)
 #define UVCIOC_CTRL_GET                _IOWR('U', 3, struct uvc_xu_control)
 #define UVCIOC_CTRL_SET                _IOW('U', 4, struct uvc_xu_control)
@@ -198,11 +215,6 @@ struct uvc_streaming_control {
        __u8  bMaxVersion;
 };
 
-struct uvc_menu_info {
-       __u32 value;
-       __u8 name[32];
-};
-
 struct uvc_control_info {
        struct list_head list;
        struct list_head mappings;
@@ -625,6 +637,7 @@ extern int uvc_ctrl_init_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 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_commit(struct uvc_video_chain *chain, int rollback);