}
 }
 
+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;
 
 
 #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
  */
                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)
                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);
 
 #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 | \
        __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];
        __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 {
 };
 
 #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)
        __u8  bMaxVersion;
 };
 
-struct uvc_menu_info {
-       __u32 value;
-       __u8 name[32];
-};
-
 struct uvc_control_info {
        struct list_head list;
        struct list_head mappings;
 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);