From cf664a6458b254ce665d129c0960cff4f32b91f3 Mon Sep 17 00:00:00 2001 From: Philippe De Muyter Date: Mon, 9 Jan 2006 15:24:59 -0200 Subject: [PATCH] V4L/DVB (3120): Adds 32-bit compatibility for v4l2 framegrabber ioctls. - Adds 32-bit compatibility for v4l2 framegrabber ioctls. Signed-off-by: Philippe De Muyter Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/compat_ioctl32.c | 388 ++++++++++++++++++++++++++- 1 file changed, 376 insertions(+), 12 deletions(-) diff --git a/drivers/media/video/compat_ioctl32.c b/drivers/media/video/compat_ioctl32.c index 5472d36d73ff..4b14942dd91c 100644 --- a/drivers/media/video/compat_ioctl32.c +++ b/drivers/media/video/compat_ioctl32.c @@ -1,6 +1,21 @@ +/* + * ioctl32.c: Conversion between 32bit and 64bit native ioctls. + * Separated from fs stuff by Arnd Bergmann + * + * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com) + * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) + * Copyright (C) 2001,2002 Andi Kleen, SuSE Labs + * Copyright (C) 2003 Pavel Machek (pavel@suse.cz) + * Copyright (C) 2005 Philippe De Muyter (phdm@macqel.be) + * + * These routines maintain argument size conversion between 32bit and 64bit + * ioctls. + */ + #include #include #include +#include #include #include @@ -15,12 +30,9 @@ struct video_tuner32 { static int get_video_tuner32(struct video_tuner *kp, struct video_tuner32 __user *up) { - int i; - if(get_user(kp->tuner, &up->tuner)) return -EFAULT; - for(i = 0; i < 32; i++) - __get_user(kp->name[i], &up->name[i]); + __copy_from_user(kp->name, up->name, 32); __get_user(kp->rangelow, &up->rangelow); __get_user(kp->rangehigh, &up->rangehigh); __get_user(kp->flags, &up->flags); @@ -31,12 +43,9 @@ static int get_video_tuner32(struct video_tuner *kp, struct video_tuner32 __user static int put_video_tuner32(struct video_tuner *kp, struct video_tuner32 __user *up) { - int i; - if(put_user(kp->tuner, &up->tuner)) return -EFAULT; - for(i = 0; i < 32; i++) - __put_user(kp->name[i], &up->name[i]); + __copy_to_user(up->name, kp->name, 32); __put_user(kp->rangelow, &up->rangelow); __put_user(kp->rangehigh, &up->rangehigh); __put_user(kp->flags, &up->flags); @@ -123,6 +132,265 @@ static int put_video_window32(struct video_window *kp, struct video_window32 __u return 0; } +struct v4l2_clip32 +{ + struct v4l2_rect c; + compat_caddr_t next; +}; + +struct v4l2_window32 +{ + struct v4l2_rect w; + enum v4l2_field field; + __u32 chromakey; + compat_caddr_t clips; /* actually struct v4l2_clip32 * */ + __u32 clipcount; + compat_caddr_t bitmap; +}; + +static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up) +{ + if (copy_from_user(&kp->w, &up->w, sizeof(up->w))) + return -EFAULT; + __get_user(kp->field, &up->field); + __get_user(kp->chromakey, &up->chromakey); + __get_user(kp->clipcount, &up->clipcount); + if (kp->clipcount > 2048) + return -EINVAL; + if (kp->clipcount) { + struct v4l2_clip32 *uclips = compat_ptr(up->clips); + struct v4l2_clip *kclips; + int n = kp->clipcount; + + kclips = compat_alloc_user_space(n * sizeof(struct v4l2_clip)); + kp->clips = kclips; + while (--n >= 0) { + copy_from_user(&kclips->c, &uclips->c, sizeof(uclips->c)); + kclips->next = n ? kclips + 1 : 0; + uclips += 1; + kclips += 1; + } + } else + kp->clips = 0; + return 0; +} + +static int put_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up) +{ + if (copy_to_user(&up->w, &kp->w, sizeof(up->w))) + return -EFAULT; + __put_user(kp->field, &up->field); + __put_user(kp->chromakey, &up->chromakey); + __put_user(kp->clipcount, &up->clipcount); + return 0; +} + +static inline int get_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pix_format __user *up) +{ + return copy_from_user(kp, up, sizeof(struct v4l2_pix_format)); +} + +static inline int put_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pix_format __user *up) +{ + return copy_to_user(up, kp, sizeof(struct v4l2_pix_format)); +} + +static inline int get_v4l2_vbi_format(struct v4l2_vbi_format *kp, struct v4l2_vbi_format __user *up) +{ + return copy_from_user(kp, up, sizeof(struct v4l2_vbi_format)); +} + +static inline int put_v4l2_vbi_format(struct v4l2_vbi_format *kp, struct v4l2_vbi_format __user *up) +{ + return copy_to_user(up, kp, sizeof(struct v4l2_vbi_format)); +} + +struct v4l2_format32 +{ + enum v4l2_buf_type type; + union + { + struct v4l2_pix_format pix; // V4L2_BUF_TYPE_VIDEO_CAPTURE + struct v4l2_window32 win; // V4L2_BUF_TYPE_VIDEO_OVERLAY + struct v4l2_vbi_format vbi; // V4L2_BUF_TYPE_VBI_CAPTURE + __u8 raw_data[200]; // user-defined + } fmt; +}; + +static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up) +{ + if(get_user(kp->type, &up->type)) + return -EFAULT; + switch (kp->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return get_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix); + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + return get_v4l2_window32(&kp->fmt.win, &up->fmt.win); + case V4L2_BUF_TYPE_VBI_CAPTURE: + return get_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi); + default: + printk("compat_ioctl : unexpected VIDIOC_FMT type %d\n", + kp->type); + return -ENXIO; + } +} + +static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up) +{ + if(put_user(kp->type, &up->type)) + return -EFAULT; + switch (kp->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return put_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix); + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + return put_v4l2_window32(&kp->fmt.win, &up->fmt.win); + case V4L2_BUF_TYPE_VBI_CAPTURE: + return put_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi); + default: + return -ENXIO; + } +} + +struct v4l2_standard32 +{ + __u32 index; + __u32 id[2]; /* __u64 would get the alignment wrong */ + __u8 name[24]; + struct v4l2_fract frameperiod; /* Frames, not fields */ + __u32 framelines; + __u32 reserved[4]; +}; + +static int get_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 __user *up) +{ + /* other fields are not set by the user, nor used by the driver */ + return get_user(kp->index, &up->index); +} + +static int put_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 __user *up) +{ + if(put_user(kp->index, &up->index)) + return -EFAULT; + __copy_to_user(up->id, &kp->id, sizeof(__u64)); + __copy_to_user(up->name, kp->name, 24); + __put_user(kp->frameperiod, &up->frameperiod); + __put_user(kp->framelines, &up->framelines); + __copy_to_user(up->reserved, kp->reserved, 4 * sizeof(__u32)); + return 0; +} + +struct v4l2_buffer32 +{ + __u32 index; + enum v4l2_buf_type type; + __u32 bytesused; + __u32 flags; + enum v4l2_field field; + struct compat_timeval timestamp; + struct v4l2_timecode timecode; + __u32 sequence; + + /* memory location */ + enum v4l2_memory memory; + union { + __u32 offset; + compat_long_t userptr; + } m; + __u32 length; + __u32 input; + __u32 reserved; +}; + +static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user *up) +{ + + if (get_user(kp->index, &up->index)) + return -EFAULT; + __get_user(kp->type, &up->type); + __get_user(kp->flags, &up->flags); + __get_user(kp->memory, &up->memory); + __get_user(kp->input, &up->input); + switch(kp->memory) { + case V4L2_MEMORY_MMAP: + break; + case V4L2_MEMORY_USERPTR: + { + unsigned long tmp = (unsigned long)compat_ptr(up->m.userptr); + + __get_user(kp->length, &up->length); + __get_user(kp->m.userptr, &tmp); + } + break; + case V4L2_MEMORY_OVERLAY: + __get_user(kp->m.offset, &up->m.offset); + break; + } + return 0; +} + +static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user *up) +{ + if (put_user(kp->index, &up->index)) + return -EFAULT; + __put_user(kp->type, &up->type); + __put_user(kp->flags, &up->flags); + __put_user(kp->memory, &up->memory); + __put_user(kp->input, &up->input); + switch(kp->memory) { + case V4L2_MEMORY_MMAP: + __put_user(kp->length, &up->length); + __put_user(kp->m.offset, &up->m.offset); + break; + case V4L2_MEMORY_USERPTR: + __put_user(kp->length, &up->length); + __put_user(kp->m.userptr, &up->m.userptr); + break; + case V4L2_MEMORY_OVERLAY: + __put_user(kp->m.offset, &up->m.offset); + break; + } + __put_user(kp->bytesused, &up->bytesused); + __put_user(kp->field, &up->field); + __put_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec); + __put_user(kp->timestamp.tv_usec, &up->timestamp.tv_usec); + __copy_to_user(&up->timecode, &kp->timecode, sizeof(struct v4l2_timecode)); + __put_user(kp->sequence, &up->sequence); + __put_user(kp->reserved, &up->reserved); + return 0; +} + +struct v4l2_framebuffer32 +{ + __u32 capability; + __u32 flags; + compat_caddr_t base; + struct v4l2_pix_format fmt; +}; + +static int put_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_framebuffer32 __user *up) +{ + u32 tmp = (u32)((unsigned long)kp->base); + + if(put_user(tmp, &up->base)) + return -EFAULT; + __put_user(kp->capability, &up->capability); + __put_user(kp->flags, &up->flags); + put_v4l2_pix_format(&kp->fmt, &up->fmt); + return 0; +} + +struct v4l2_input32 /* identical layout, but different size */ +{ + __u32 index; /* Which input */ + __u8 name[32]; /* Label */ + __u32 type; /* Type of input */ + __u32 audioset; /* Associated audios (bitfield) */ + __u32 tuner; /* Associated tuner */ + __u32 std[2]; /* __u64 would get the padding wrong */ + __u32 status; + __u32 reserved[4]; +}; + #define VIDIOCGTUNER32 _IOWR('v',4, struct video_tuner32) #define VIDIOCSTUNER32 _IOW('v',5, struct video_tuner32) #define VIDIOCGWIN32 _IOR('v',9, struct video_window32) @@ -132,6 +400,24 @@ static int put_video_window32(struct video_window *kp, struct video_window32 __u #define VIDIOCGFREQ32 _IOR('v',14, u32) #define VIDIOCSFREQ32 _IOW('v',15, u32) +#define VIDIOC_G_FMT32 _IOWR ('V', 4, struct v4l2_format32) +#define VIDIOC_S_FMT32 _IOWR ('V', 5, struct v4l2_format32) +#define VIDIOC_QUERYBUF32 _IOWR ('V', 9, struct v4l2_buffer32) +#define VIDIOC_G_FBUF32 _IOR ('V', 10, struct v4l2_framebuffer32) +/* VIDIOC_OVERLAY is now _IOW, but was _IOWR */ +#define VIDIOC_OVERLAY32 _IOWR ('V', 14, compat_int_t) +#define VIDIOC_QBUF32 _IOWR ('V', 15, struct v4l2_buffer32) +#define VIDIOC_DQBUF32 _IOWR ('V', 17, struct v4l2_buffer32) +#define VIDIOC_STREAMON32 _IOW ('V', 18, compat_int_t) +#define VIDIOC_STREAMOFF32 _IOW ('V', 19, compat_int_t) +#define VIDIOC_ENUMSTD32 _IOWR ('V', 25, struct v4l2_standard32) +#define VIDIOC_ENUMINPUT32 _IOWR ('V', 26, struct v4l2_input32) +/* VIDIOC_S_CTRL is now _IOWR, but was _IOW */ +#define VIDIOC_S_CTRL32 _IOW ('V', 28, struct v4l2_control) +#define VIDIOC_G_INPUT32 _IOR ('V', 38, compat_int_t) +#define VIDIOC_S_INPUT32 _IOWR ('V', 39, compat_int_t) +#define VIDIOC_TRY_FMT32 _IOWR ('V', 64, struct v4l2_format32) + enum { MaxClips = (~0U-sizeof(struct video_window))/sizeof(struct video_clip) }; @@ -198,10 +484,14 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg struct video_tuner vt; struct video_buffer vb; struct video_window vw; + struct v4l2_format v2f; + struct v4l2_buffer v2b; + struct v4l2_framebuffer v2fb; + struct v4l2_standard v2s; unsigned long vx; } karg; - mm_segment_t old_fs = get_fs(); void __user *up = compat_ptr(arg); + int compatible_arg = 1; int err = 0; /* First, convert the command. */ @@ -213,29 +503,82 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg case VIDIOCSFBUF32: cmd = VIDIOCSFBUF; break; case VIDIOCGFREQ32: cmd = VIDIOCGFREQ; break; case VIDIOCSFREQ32: cmd = VIDIOCSFREQ; break; + case VIDIOC_G_FMT32: cmd = VIDIOC_G_FMT; break; + case VIDIOC_S_FMT32: cmd = VIDIOC_S_FMT; break; + case VIDIOC_QUERYBUF32: cmd = VIDIOC_QUERYBUF; break; + case VIDIOC_QBUF32: cmd = VIDIOC_QBUF; break; + case VIDIOC_DQBUF32: cmd = VIDIOC_DQBUF; break; + case VIDIOC_STREAMON32: cmd = VIDIOC_STREAMON; break; + case VIDIOC_STREAMOFF32: cmd = VIDIOC_STREAMOFF; break; + case VIDIOC_G_FBUF32: cmd = VIDIOC_G_FBUF; break; + case VIDIOC_OVERLAY32: cmd = VIDIOC_OVERLAY; break; + case VIDIOC_ENUMSTD32: cmd = VIDIOC_ENUMSTD; break; + case VIDIOC_ENUMINPUT32: cmd = VIDIOC_ENUMINPUT; break; + case VIDIOC_S_CTRL32: cmd = VIDIOC_S_CTRL; break; + case VIDIOC_G_INPUT32: cmd = VIDIOC_G_INPUT; break; + case VIDIOC_S_INPUT32: cmd = VIDIOC_S_INPUT; break; + case VIDIOC_TRY_FMT32: cmd = VIDIOC_TRY_FMT; break; }; switch(cmd) { case VIDIOCSTUNER: case VIDIOCGTUNER: err = get_video_tuner32(&karg.vt, up); + compatible_arg = 0; + break; case VIDIOCSFBUF: err = get_video_buffer32(&karg.vb, up); + compatible_arg = 0; break; case VIDIOCSFREQ: + case VIDIOC_S_INPUT: + case VIDIOC_OVERLAY: err = get_user(karg.vx, (u32 __user *)up); + compatible_arg = 0; break; }; + + case VIDIOC_G_FMT: + case VIDIOC_S_FMT: + case VIDIOC_TRY_FMT: + err = get_v4l2_format32(&karg.v2f, up); + compatible_arg = 0; + break; + + case VIDIOC_QUERYBUF: + case VIDIOC_QBUF: + case VIDIOC_DQBUF: + err = get_v4l2_buffer32(&karg.v2b, up); + compatible_arg = 0; + break; + + case VIDIOC_ENUMSTD: + err = get_v4l2_standard32(&karg.v2s, up); + compatible_arg = 0; + break; + + case VIDIOCGWIN: + case VIDIOCGFBUF: + case VIDIOCGFREQ: + case VIDIOC_G_FBUF: + case VIDIOC_G_INPUT: + compatible_arg = 0; + if(err) goto out; - set_fs(KERNEL_DS); - err = native_ioctl(file, cmd, (unsigned long)&karg); - set_fs(old_fs); + if(compatible_arg) + err = sys_ioctl(fd, cmd, (unsigned long)up); + else { + mm_segment_t old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&karg); + set_fs(old_fs); + } if(err == 0) { switch(cmd) { case VIDIOCGTUNER: @@ -250,7 +593,28 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg err = put_video_buffer32(&karg.vb, up); break; + case VIDIOC_G_FBUF: + err = put_v4l2_framebuffer32(&karg.v2fb, up); + break; + + case VIDIOC_G_FMT: + case VIDIOC_S_FMT: + case VIDIOC_TRY_FMT: + err = put_v4l2_format32(&karg.v2f, up); + break; + + case VIDIOC_QUERYBUF: + case VIDIOC_QBUF: + case VIDIOC_DQBUF: + err = put_v4l2_buffer32(&karg.v2b, up); + break; + + case VIDIOC_ENUMSTD: + err = put_v4l2_standard32(&karg.v2s, up); + break; + case VIDIOCGFREQ: + case VIDIOC_G_INPUT: err = put_user(((u32)karg.vx), (u32 __user *)up); break; }; -- 2.20.1