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

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

[media] uvcvideo: Implement compat_ioctl32 for custom ioctls



Support 32-bit/64-bit compatibility for the the UVCIOC_ ioctls.

Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent a3ec69b7
Loading
Loading
Loading
Loading
+205 −0
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
 *
 */

#include <linux/compat.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/list.h>
@@ -1030,6 +1031,207 @@ static long uvc_v4l2_ioctl(struct file *file,
	return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl);
}

#ifdef CONFIG_COMPAT
struct uvc_xu_control_mapping32 {
	__u32 id;
	__u8 name[32];
	__u8 entity[16];
	__u8 selector;

	__u8 size;
	__u8 offset;
	__u32 v4l2_type;
	__u32 data_type;

	compat_caddr_t menu_info;
	__u32 menu_count;

	__u32 reserved[4];
};

static int uvc_v4l2_get_xu_mapping(struct uvc_xu_control_mapping *kp,
			const struct uvc_xu_control_mapping32 __user *up)
{
	struct uvc_menu_info __user *umenus;
	struct uvc_menu_info __user *kmenus;
	compat_caddr_t p;

	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
	    __copy_from_user(kp, up, offsetof(typeof(*up), menu_info)) ||
	    __get_user(kp->menu_count, &up->menu_count))
		return -EFAULT;

	memset(kp->reserved, 0, sizeof(kp->reserved));

	if (kp->menu_count == 0) {
		kp->menu_info = NULL;
		return 0;
	}

	if (__get_user(p, &up->menu_info))
		return -EFAULT;
	umenus = compat_ptr(p);
	if (!access_ok(VERIFY_READ, umenus, kp->menu_count * sizeof(*umenus)))
		return -EFAULT;

	kmenus = compat_alloc_user_space(kp->menu_count * sizeof(*kmenus));
	if (kmenus == NULL)
		return -EFAULT;
	kp->menu_info = kmenus;

	if (copy_in_user(kmenus, umenus, kp->menu_count * sizeof(*umenus)))
		return -EFAULT;

	return 0;
}

static int uvc_v4l2_put_xu_mapping(const struct uvc_xu_control_mapping *kp,
			struct uvc_xu_control_mapping32 __user *up)
{
	struct uvc_menu_info __user *umenus;
	struct uvc_menu_info __user *kmenus = kp->menu_info;
	compat_caddr_t p;

	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
	    __copy_to_user(up, kp, offsetof(typeof(*up), menu_info)) ||
	    __put_user(kp->menu_count, &up->menu_count))
		return -EFAULT;

	__clear_user(up->reserved, sizeof(up->reserved));

	if (kp->menu_count == 0)
		return 0;

	if (get_user(p, &up->menu_info))
		return -EFAULT;
	umenus = compat_ptr(p);
	if (!access_ok(VERIFY_WRITE, umenus, kp->menu_count * sizeof(*umenus)))
		return -EFAULT;

	if (copy_in_user(umenus, kmenus, kp->menu_count * sizeof(*umenus)))
		return -EFAULT;

	return 0;
}

struct uvc_xu_control_query32 {
	__u8 unit;
	__u8 selector;
	__u8 query;
	__u16 size;
	compat_caddr_t data;
};

static int uvc_v4l2_get_xu_query(struct uvc_xu_control_query *kp,
			const struct uvc_xu_control_query32 __user *up)
{
	u8 __user *udata;
	u8 __user *kdata;
	compat_caddr_t p;

	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
	    __copy_from_user(kp, up, offsetof(typeof(*up), data)))
		return -EFAULT;

	if (kp->size == 0) {
		kp->data = NULL;
		return 0;
	}

	if (__get_user(p, &up->data))
		return -EFAULT;
	udata = compat_ptr(p);
	if (!access_ok(VERIFY_READ, udata, kp->size))
		return -EFAULT;

	kdata = compat_alloc_user_space(kp->size);
	if (kdata == NULL)
		return -EFAULT;
	kp->data = kdata;

	if (copy_in_user(kdata, udata, kp->size))
		return -EFAULT;

	return 0;
}

static int uvc_v4l2_put_xu_query(const struct uvc_xu_control_query *kp,
			struct uvc_xu_control_query32 __user *up)
{
	u8 __user *udata;
	u8 __user *kdata = kp->data;
	compat_caddr_t p;

	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
	    __copy_to_user(up, kp, offsetof(typeof(*up), data)))
		return -EFAULT;

	if (kp->size == 0)
		return 0;

	if (get_user(p, &up->data))
		return -EFAULT;
	udata = compat_ptr(p);
	if (!access_ok(VERIFY_READ, udata, kp->size))
		return -EFAULT;

	if (copy_in_user(udata, kdata, kp->size))
		return -EFAULT;

	return 0;
}

#define UVCIOC_CTRL_MAP32	_IOWR('u', 0x20, struct uvc_xu_control_mapping32)
#define UVCIOC_CTRL_QUERY32	_IOWR('u', 0x21, struct uvc_xu_control_query32)

static long uvc_v4l2_compat_ioctl32(struct file *file,
		     unsigned int cmd, unsigned long arg)
{
	union {
		struct uvc_xu_control_mapping xmap;
		struct uvc_xu_control_query xqry;
	} karg;
	void __user *up = compat_ptr(arg);
	mm_segment_t old_fs;
	long ret;

	switch (cmd) {
	case UVCIOC_CTRL_MAP32:
		cmd = UVCIOC_CTRL_MAP;
		ret = uvc_v4l2_get_xu_mapping(&karg.xmap, up);
		break;

	case UVCIOC_CTRL_QUERY32:
		cmd = UVCIOC_CTRL_QUERY;
		ret = uvc_v4l2_get_xu_query(&karg.xqry, up);
		break;

	default:
		return -ENOIOCTLCMD;
	}

	old_fs = get_fs();
	set_fs(KERNEL_DS);
	ret = uvc_v4l2_ioctl(file, cmd, (unsigned long)&karg);
	set_fs(old_fs);

	if (ret < 0)
		return ret;

	switch (cmd) {
	case UVCIOC_CTRL_MAP:
		ret = uvc_v4l2_put_xu_mapping(&karg.xmap, up);
		break;

	case UVCIOC_CTRL_QUERY:
		ret = uvc_v4l2_put_xu_query(&karg.xqry, up);
		break;
	}

	return ret;
}
#endif

static ssize_t uvc_v4l2_read(struct file *file, char __user *data,
		    size_t count, loff_t *ppos)
{
@@ -1076,6 +1278,9 @@ const struct v4l2_file_operations uvc_fops = {
	.open		= uvc_v4l2_open,
	.release	= uvc_v4l2_release,
	.unlocked_ioctl	= uvc_v4l2_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl32	= uvc_v4l2_compat_ioctl32,
#endif
	.read		= uvc_v4l2_read,
	.mmap		= uvc_v4l2_mmap,
	.poll		= uvc_v4l2_poll,