diff options
Diffstat (limited to 'drivers/media/video/uvc/uvc_v4l2.c')
| -rw-r--r-- | drivers/media/video/uvc/uvc_v4l2.c | 103 | 
1 files changed, 76 insertions, 27 deletions
diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index 7c9ab293349..86db32697b8 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   */ @@ -451,7 +516,7 @@ static int uvc_v4l2_open(struct file *file)  static int uvc_v4l2_release(struct file *file)  { -	struct uvc_fh *handle = (struct uvc_fh *)file->private_data; +	struct uvc_fh *handle = file->private_data;  	struct uvc_streaming *stream = handle->stream;  	uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_release\n"); @@ -482,7 +547,7 @@ static int uvc_v4l2_release(struct file *file)  static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)  {  	struct video_device *vdev = video_devdata(file); -	struct uvc_fh *handle = (struct uvc_fh *)file->private_data; +	struct uvc_fh *handle = file->private_data;  	struct uvc_video_chain *chain = handle->chain;  	struct uvc_streaming *stream = handle->stream;  	long ret = 0; @@ -963,6 +1028,9 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)  		if (!capable(CAP_SYS_ADMIN))  			return -EPERM; +		if (xinfo->size == 0) +			return -EINVAL; +  		info = kzalloc(sizeof *info, GFP_KERNEL);  		if (info == NULL)  			return -ENOMEM; @@ -974,7 +1042,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 +1051,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); @@ -1067,7 +1116,7 @@ static const struct vm_operations_struct uvc_vm_ops = {  static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma)  { -	struct uvc_fh *handle = (struct uvc_fh *)file->private_data; +	struct uvc_fh *handle = file->private_data;  	struct uvc_streaming *stream = handle->stream;  	struct uvc_video_queue *queue = &stream->queue;  	struct uvc_buffer *uninitialized_var(buffer); @@ -1122,7 +1171,7 @@ done:  static unsigned int uvc_v4l2_poll(struct file *file, poll_table *wait)  { -	struct uvc_fh *handle = (struct uvc_fh *)file->private_data; +	struct uvc_fh *handle = file->private_data;  	struct uvc_streaming *stream = handle->stream;  	uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_poll\n");  |