diff options
Diffstat (limited to 'drivers/media/usb/uvc/uvc_v4l2.c')
-rw-r--r-- | drivers/media/usb/uvc/uvc_v4l2.c | 111 |
1 files changed, 80 insertions, 31 deletions
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index f4d4c33b6dfb..35453f81c1d9 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -6,6 +6,7 @@ * Laurent Pinchart (laurent.pinchart@ideasonboard.com) */ +#include <linux/bits.h> #include <linux/compat.h> #include <linux/kernel.h> #include <linux/list.h> @@ -25,14 +26,84 @@ #include "uvcvideo.h" +static int uvc_control_add_xu_mapping(struct uvc_video_chain *chain, + struct uvc_control_mapping *map, + const struct uvc_xu_control_mapping *xmap) +{ + unsigned int i; + size_t size; + int ret; + + /* + * Prevent excessive memory consumption, as well as integer + * overflows. + */ + if (xmap->menu_count == 0 || + xmap->menu_count > UVC_MAX_CONTROL_MENU_ENTRIES) + return -EINVAL; + + map->menu_names = NULL; + map->menu_mapping = NULL; + + map->menu_mask = BIT_MASK(xmap->menu_count); + + size = xmap->menu_count * sizeof(*map->menu_mapping); + map->menu_mapping = kzalloc(size, GFP_KERNEL); + if (!map->menu_mapping) { + ret = -ENOMEM; + goto done; + } + + for (i = 0; i < xmap->menu_count ; i++) { + if (copy_from_user((u32 *)&map->menu_mapping[i], + &xmap->menu_info[i].value, + sizeof(map->menu_mapping[i]))) { + ret = -EACCES; + goto done; + } + } + + /* + * Always use the standard naming if available, otherwise copy the + * names supplied by userspace. + */ + if (!v4l2_ctrl_get_menu(map->id)) { + size = xmap->menu_count * sizeof(map->menu_names[0]); + map->menu_names = kzalloc(size, GFP_KERNEL); + if (!map->menu_names) { + ret = -ENOMEM; + goto done; + } + + for (i = 0; i < xmap->menu_count ; i++) { + /* sizeof(names[i]) - 1: to take care of \0 */ + if (copy_from_user((char *)map->menu_names[i], + xmap->menu_info[i].name, + sizeof(map->menu_names[i]) - 1)) { + ret = -EACCES; + goto done; + } + } + } + + ret = uvc_ctrl_add_mapping(chain, map); + +done: + kfree(map->menu_names); + map->menu_names = NULL; + kfree(map->menu_mapping); + map->menu_mapping = NULL; + + return ret; +} + /* ------------------------------------------------------------------------ * UVC ioctls */ -static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain, - struct uvc_xu_control_mapping *xmap) +static int uvc_ioctl_xu_ctrl_map(struct uvc_video_chain *chain, + struct uvc_xu_control_mapping *xmap) { struct uvc_control_mapping *map; - unsigned int size; int ret; map = kzalloc(sizeof(*map), GFP_KERNEL); @@ -60,39 +131,20 @@ static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain, case V4L2_CTRL_TYPE_INTEGER: case V4L2_CTRL_TYPE_BOOLEAN: case V4L2_CTRL_TYPE_BUTTON: + ret = uvc_ctrl_add_mapping(chain, map); break; case V4L2_CTRL_TYPE_MENU: - /* - * Prevent excessive memory consumption, as well as integer - * overflows. - */ - if (xmap->menu_count == 0 || - xmap->menu_count > UVC_MAX_CONTROL_MENU_ENTRIES) { - ret = -EINVAL; - goto free_map; - } - - size = xmap->menu_count * sizeof(*map->menu_info); - map->menu_info = memdup_user(xmap->menu_info, size); - if (IS_ERR(map->menu_info)) { - ret = PTR_ERR(map->menu_info); - goto free_map; - } - - map->menu_count = xmap->menu_count; + ret = uvc_control_add_xu_mapping(chain, map, xmap); break; default: uvc_dbg(chain->dev, CONTROL, "Unsupported V4L2 control type %u\n", xmap->v4l2_type); ret = -ENOTTY; - goto free_map; + break; } - ret = uvc_ctrl_add_mapping(chain, map); - - kfree(map->menu_info); free_map: kfree(map); @@ -660,8 +712,6 @@ static int uvc_ioctl_enum_fmt(struct uvc_streaming *stream, fmt->flags = 0; if (format->flags & UVC_FMT_FLAG_COMPRESSED) fmt->flags |= V4L2_FMT_FLAG_COMPRESSED; - strscpy(fmt->description, format->name, sizeof(fmt->description)); - fmt->description[sizeof(fmt->description) - 1] = 0; fmt->pixelformat = format->fcc; return 0; } @@ -1020,8 +1070,7 @@ static int uvc_ctrl_check_access(struct uvc_video_chain *chain, int ret = 0; for (i = 0; i < ctrls->count; ++ctrl, ++i) { - ret = uvc_ctrl_is_accessible(chain, ctrl->id, - ioctl == VIDIOC_G_EXT_CTRLS); + ret = uvc_ctrl_is_accessible(chain, ctrl->id, ctrls, ioctl); if (ret) break; } @@ -1316,7 +1365,7 @@ static long uvc_ioctl_default(struct file *file, void *fh, bool valid_prio, switch (cmd) { /* Dynamic controls. */ case UVCIOC_CTRL_MAP: - return uvc_ioctl_ctrl_map(chain, arg); + return uvc_ioctl_xu_ctrl_map(chain, arg); case UVCIOC_CTRL_QUERY: return uvc_xu_ctrl_query(chain, arg); @@ -1429,7 +1478,7 @@ static long uvc_v4l2_compat_ioctl32(struct file *file, ret = uvc_v4l2_get_xu_mapping(&karg.xmap, up); if (ret) return ret; - ret = uvc_ioctl_ctrl_map(handle->chain, &karg.xmap); + ret = uvc_ioctl_xu_ctrl_map(handle->chain, &karg.xmap); if (ret) return ret; ret = uvc_v4l2_put_xu_mapping(&karg.xmap, up); |