mirror of
https://github.com/torvalds/linux.git
synced 2024-11-08 13:11:45 +00:00
[media] uvcvideo: Implement compat_ioctl32 for custom ioctls
Support 32-bit/64-bit compatibility for the the UVCIOC_ ioctls. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
parent
a3ec69b707
commit
1a5e4c867c
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user