summary refs log tree commit diff
path: root/drivers
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>2010-06-18 11:31:24 -0300
committerMauro Carvalho Chehab <mchehab@redhat.com>2010-08-02 15:34:10 -0300
commitb30ece53946ad2b79304ee5cfdb18b361dc3a3fc (patch)
tree80dc24b56b1a38d03d50e8077ae275ec02391847 /drivers
parent1b4e21c4f62eae6bdcb3e7bfdfc52171a24f3689 (diff)
downloadlinux-b30ece53946ad2b79304ee5cfdb18b361dc3a3fc.tar.gz
V4L/DVB: uvcvideo: Don't use stack-based buffers for USB transfers
Data buffers on the stack are not allowed for USB I/O. Use dynamically
allocated buffers instead when querying control length and control
capabilities.

The control capabilities are now also stored in the uvc_control
structure.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/media/video/uvc/uvc_ctrl.c51
-rw-r--r--drivers/media/video/uvc/uvc_v4l2.c3
-rw-r--r--drivers/media/video/uvc/uvcvideo.h3
3 files changed, 38 insertions, 19 deletions
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c
index fa06cf512ecf..a350fad0db43 100644
--- a/drivers/media/video/uvc/uvc_ctrl.c
+++ b/drivers/media/video/uvc/uvc_ctrl.c
@@ -643,7 +643,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = {
 
 static inline __u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id)
 {
-	return ctrl->data + id * ctrl->info->size;
+	return ctrl->uvc_data + id * ctrl->info->size;
 }
 
 static inline int uvc_test_bit(const __u8 *data, int bit)
@@ -1293,13 +1293,15 @@ int uvc_ctrl_resume_device(struct uvc_device *dev)
  * Control and mapping handling
  */
 
-static void uvc_ctrl_add_ctrl(struct uvc_device *dev,
+static int uvc_ctrl_add_ctrl(struct uvc_device *dev,
 	struct uvc_control_info *info)
 {
 	struct uvc_entity *entity;
 	struct uvc_control *ctrl = NULL;
-	int ret, found = 0;
+	int ret = 0, found = 0;
 	unsigned int i;
+	u8 *uvc_info;
+	u8 *uvc_data;
 
 	list_for_each_entry(entity, &dev->entities, list) {
 		if (!uvc_entity_match_guid(entity, info->entity))
@@ -1318,56 +1320,69 @@ static void uvc_ctrl_add_ctrl(struct uvc_device *dev,
 	}
 
 	if (!found)
-		return;
+		return 0;
+
+	uvc_data = kmalloc(info->size * UVC_CTRL_DATA_LAST + 1, GFP_KERNEL);
+	if (uvc_data == NULL)
+		return -ENOMEM;
+
+	uvc_info = uvc_data + info->size * UVC_CTRL_DATA_LAST;
 
 	if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT) {
 		/* Check if the device control information and length match
 		 * the user supplied information.
 		 */
-		__le16 size;
-		__u8 _info;
-
 		ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id,
-			dev->intfnum, info->selector, (__u8 *)&size, 2);
+				     dev->intfnum, info->selector, uvc_data, 2);
 		if (ret < 0) {
 			uvc_trace(UVC_TRACE_CONTROL,
 				"GET_LEN failed on control %pUl/%u (%d).\n",
 				info->entity, info->selector, ret);
-			return;
+			goto done;
 		}
 
-		if (info->size != le16_to_cpu(size)) {
+		if (info->size != le16_to_cpu(*(__le16 *)uvc_data)) {
 			uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u size "
 				"doesn't match user supplied value.\n",
 				info->entity, info->selector);
-			return;
+			ret = -EINVAL;
+			goto done;
 		}
 
 		ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id,
-				     dev->intfnum, info->selector, &_info, 1);
+				     dev->intfnum, info->selector, uvc_info, 1);
 		if (ret < 0) {
 			uvc_trace(UVC_TRACE_CONTROL,
 				"GET_INFO failed on control %pUl/%u (%d).\n",
 				info->entity, info->selector, ret);
-			return;
+			goto done;
 		}
 
 		if (((info->flags & UVC_CONTROL_GET_CUR) &&
-		    !(_info & UVC_CONTROL_CAP_GET)) ||
+		    !(*uvc_info & UVC_CONTROL_CAP_GET)) ||
 		    ((info->flags & UVC_CONTROL_SET_CUR) &&
-		    !(_info & UVC_CONTROL_CAP_SET))) {
+		    !(*uvc_info & UVC_CONTROL_CAP_SET))) {
 			uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u flags "
 				"don't match supported operations.\n",
 				info->entity, info->selector);
-			return;
+			ret = -EINVAL;
+			goto done;
 		}
 	}
 
 	ctrl->info = info;
-	ctrl->data = kmalloc(ctrl->info->size * UVC_CTRL_DATA_LAST, GFP_KERNEL);
+	ctrl->uvc_data = uvc_data;
+	ctrl->uvc_info = uvc_info;
+
 	uvc_trace(UVC_TRACE_CONTROL, "Added control %pUl/%u to device %s "
 		"entity %u\n", ctrl->info->entity, ctrl->info->selector,
 		dev->udev->devpath, entity->id);
+
+done:
+	if (ret < 0)
+		kfree(uvc_data);
+
+	return ret;
 }
 
 /*
@@ -1600,7 +1615,7 @@ void uvc_ctrl_cleanup_device(struct uvc_device *dev)
 
 	list_for_each_entry(entity, &dev->entities, list) {
 		for (i = 0; i < entity->ncontrols; ++i)
-			kfree(entity->controls[i].data);
+			kfree(entity->controls[i].uvc_data);
 
 		kfree(entity->controls);
 	}
diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c
index 485a89912cef..369ce06be035 100644
--- a/drivers/media/video/uvc/uvc_v4l2.c
+++ b/drivers/media/video/uvc/uvc_v4l2.c
@@ -1028,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;
diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h
index 14f77e42fd45..47b20e7e3786 100644
--- a/drivers/media/video/uvc/uvcvideo.h
+++ b/drivers/media/video/uvc/uvcvideo.h
@@ -262,7 +262,8 @@ struct uvc_control {
 	     modified : 1,
 	     cached : 1;
 
-	__u8 *data;
+	__u8 *uvc_data;
+	__u8 *uvc_info;
 };
 
 struct uvc_format_desc {