Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b30ece53 authored by Laurent Pinchart's avatar Laurent Pinchart Committed by Mauro Carvalho Chehab
Browse files

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: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 1b4e21c4
Loading
Loading
Loading
Loading
+33 −18
Original line number Diff line number Diff line
@@ -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);
	}
+3 −0
Original line number Diff line number Diff line
@@ -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;
+2 −1
Original line number Diff line number Diff line
@@ -262,7 +262,8 @@ struct uvc_control {
	     modified : 1,
	     cached : 1;

	__u8 *data;
	__u8 *uvc_data;
	__u8 *uvc_info;
};

struct uvc_format_desc {