Commit 561474c2 authored by Laurent Pinchart's avatar Laurent Pinchart Committed by Mauro Carvalho Chehab

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: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 3653639e
...@@ -1606,6 +1606,28 @@ void uvc_ctrl_cleanup_device(struct uvc_device *dev) ...@@ -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) void uvc_ctrl_init(void)
{ {
struct uvc_control_info *ctrl = uvc_ctrls; struct uvc_control_info *ctrl = uvc_ctrls;
......
...@@ -2260,6 +2260,7 @@ static int __init uvc_init(void) ...@@ -2260,6 +2260,7 @@ static int __init uvc_init(void)
static void __exit uvc_cleanup(void) static void __exit uvc_cleanup(void)
{ {
usb_deregister(&uvc_driver.driver); usb_deregister(&uvc_driver.driver);
uvc_ctrl_cleanup();
} }
module_init(uvc_init); module_init(uvc_init);
......
...@@ -28,6 +28,71 @@ ...@@ -28,6 +28,71 @@
#include "uvcvideo.h" #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 * V4L2 interface
*/ */
...@@ -974,7 +1039,8 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ...@@ -974,7 +1039,8 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
info->flags = xinfo->flags; info->flags = xinfo->flags;
info->flags |= UVC_CONTROL_GET_MIN | UVC_CONTROL_GET_MAX | 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); ret = uvc_ctrl_add_info(info);
if (ret < 0) if (ret < 0)
...@@ -982,32 +1048,12 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ...@@ -982,32 +1048,12 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
break; break;
} }
case UVCIOC_CTRL_MAP_OLD:
case UVCIOC_CTRL_MAP: case UVCIOC_CTRL_MAP:
{
struct uvc_xu_control_mapping *xmap = arg;
struct uvc_control_mapping *map;
if (!capable(CAP_SYS_ADMIN)) if (!capable(CAP_SYS_ADMIN))
return -EPERM; return -EPERM;
map = kzalloc(sizeof *map, GFP_KERNEL); return uvc_ioctl_ctrl_map(arg, cmd == UVCIOC_CTRL_MAP_OLD);
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;
}
case UVCIOC_CTRL_GET: case UVCIOC_CTRL_GET:
return uvc_xu_ctrl_query(chain, arg, 0); return uvc_xu_ctrl_query(chain, arg, 0);
......
...@@ -27,6 +27,8 @@ ...@@ -27,6 +27,8 @@
#define UVC_CONTROL_RESTORE (1 << 6) #define UVC_CONTROL_RESTORE (1 << 6)
/* Control can be updated by the camera. */ /* Control can be updated by the camera. */
#define UVC_CONTROL_AUTO_UPDATE (1 << 7) #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 | \ #define UVC_CONTROL_GET_RANGE (UVC_CONTROL_GET_CUR | UVC_CONTROL_GET_MIN | \
UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES | \ UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES | \
...@@ -40,6 +42,15 @@ struct uvc_xu_control_info { ...@@ -40,6 +42,15 @@ struct uvc_xu_control_info {
__u32 flags; __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 { struct uvc_xu_control_mapping {
__u32 id; __u32 id;
__u8 name[32]; __u8 name[32];
...@@ -50,6 +61,11 @@ struct uvc_xu_control_mapping { ...@@ -50,6 +61,11 @@ struct uvc_xu_control_mapping {
__u8 offset; __u8 offset;
enum v4l2_ctrl_type v4l2_type; enum v4l2_ctrl_type v4l2_type;
__u32 data_type; __u32 data_type;
struct uvc_menu_info __user *menu_info;
__u32 menu_count;
__u32 reserved[4];
}; };
struct uvc_xu_control { struct uvc_xu_control {
...@@ -60,6 +76,7 @@ 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_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_MAP _IOWR('U', 2, struct uvc_xu_control_mapping)
#define UVCIOC_CTRL_GET _IOWR('U', 3, struct uvc_xu_control) #define UVCIOC_CTRL_GET _IOWR('U', 3, struct uvc_xu_control)
#define UVCIOC_CTRL_SET _IOW('U', 4, struct uvc_xu_control) #define UVCIOC_CTRL_SET _IOW('U', 4, struct uvc_xu_control)
...@@ -198,11 +215,6 @@ struct uvc_streaming_control { ...@@ -198,11 +215,6 @@ struct uvc_streaming_control {
__u8 bMaxVersion; __u8 bMaxVersion;
}; };
struct uvc_menu_info {
__u32 value;
__u8 name[32];
};
struct uvc_control_info { struct uvc_control_info {
struct list_head list; struct list_head list;
struct list_head mappings; struct list_head mappings;
...@@ -625,6 +637,7 @@ extern int uvc_ctrl_init_device(struct uvc_device *dev); ...@@ -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 void uvc_ctrl_cleanup_device(struct uvc_device *dev);
extern int uvc_ctrl_resume_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_init(void);
extern void uvc_ctrl_cleanup(void);
extern int uvc_ctrl_begin(struct uvc_video_chain *chain); extern int uvc_ctrl_begin(struct uvc_video_chain *chain);
extern int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback); extern int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment