Merge branch 'drm-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/airlied...
[safe/jmp/linux-2.6] / drivers / media / video / v4l1-compat.c
index 88beb5a..50e1ff9 100644 (file)
  *     as published by the Free Software Foundation; either version
  *     2 of the License, or (at your option) any later version.
  *
- * Author:     Bill Dirks <bdirks@pacbell.net>
+ * Author:     Bill Dirks <bill@thedirks.org>
  *             et al.
  *
  */
 
-#include <linux/config.h>
 
 #include <linux/init.h>
 #include <linux/module.h>
-#include <linux/moduleparam.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
-#include <linux/smp_lock.h>
 #include <linux/mm.h>
 #include <linux/fs.h>
 #include <linux/file.h>
@@ -32,6 +29,7 @@
 #include <linux/errno.h>
 #include <linux/slab.h>
 #include <linux/videodev.h>
+#include <media/v4l2-common.h>
 
 #include <asm/uaccess.h>
 #include <asm/system.h>
@@ -128,7 +126,7 @@ set_v4l_control(struct inode            *inode,
 
 /* ----------------------------------------------------------------- */
 
-static int palette2pixelformat[] = {
+const static unsigned int palette2pixelformat[] = {
        [VIDEO_PALETTE_GREY]    = V4L2_PIX_FMT_GREY,
        [VIDEO_PALETTE_RGB555]  = V4L2_PIX_FMT_RGB555,
        [VIDEO_PALETTE_RGB565]  = V4L2_PIX_FMT_RGB565,
@@ -146,7 +144,7 @@ static int palette2pixelformat[] = {
        [VIDEO_PALETTE_YUV422P] = V4L2_PIX_FMT_YUV422P,
 };
 
-static unsigned int
+static unsigned int __pure
 palette_to_pixelformat(unsigned int palette)
 {
        if (palette < ARRAY_SIZE(palette2pixelformat))
@@ -155,8 +153,8 @@ palette_to_pixelformat(unsigned int palette)
                return 0;
 }
 
-static unsigned int
-pixelformat_to_palette(int pixelformat)
+static unsigned int __attribute_const__
+pixelformat_to_palette(unsigned int pixelformat)
 {
        int     palette = 0;
        switch (pixelformat)
@@ -305,9 +303,12 @@ v4l_compat_translate_ioctl(struct inode         *inode,
        {
                struct video_capability *cap = arg;
 
-               cap2 = kmalloc(sizeof(*cap2),GFP_KERNEL);
+               cap2 = kzalloc(sizeof(*cap2), GFP_KERNEL);
+               if (!cap2) {
+                       err = -ENOMEM;
+                       break;
+               }
                memset(cap, 0, sizeof(*cap));
-               memset(cap2, 0, sizeof(*cap2));
                memset(&fbuf2, 0, sizeof(fbuf2));
 
                err = drv(inode, file, VIDIOC_QUERYCAP, cap2);
@@ -350,6 +351,9 @@ v4l_compat_translate_ioctl(struct inode         *inode,
        {
                struct video_buffer     *buffer = arg;
 
+               memset(buffer, 0, sizeof(*buffer));
+               memset(&fbuf2, 0, sizeof(fbuf2));
+
                err = drv(inode, file, VIDIOC_G_FBUF, &fbuf2);
                if (err < 0) {
                        dprintk("VIDIOCGFBUF / VIDIOC_G_FBUF: %d\n",err);
@@ -362,7 +366,7 @@ v4l_compat_translate_ioctl(struct inode         *inode,
                switch (fbuf2.fmt.pixelformat) {
                case V4L2_PIX_FMT_RGB332:
                        buffer->depth = 8;
-                               break;
+                       break;
                case V4L2_PIX_FMT_RGB555:
                        buffer->depth = 15;
                        break;
@@ -378,9 +382,13 @@ v4l_compat_translate_ioctl(struct inode         *inode,
                default:
                        buffer->depth = 0;
                }
-               if (0 != fbuf2.fmt.bytesperline)
+               if (fbuf2.fmt.bytesperline) {
                        buffer->bytesperline = fbuf2.fmt.bytesperline;
-               else {
+                       if (!buffer->depth && buffer->width)
+                               buffer->depth   = ((fbuf2.fmt.bytesperline<<3)
+                                                 + (buffer->width-1) )
+                                                 /buffer->width;
+               } else {
                        buffer->bytesperline =
                                (buffer->width * buffer->depth + 7) & 7;
                        buffer->bytesperline >>= 3;
@@ -422,9 +430,12 @@ v4l_compat_translate_ioctl(struct inode         *inode,
        {
                struct video_window     *win = arg;
 
-               fmt2 = kmalloc(sizeof(*fmt2),GFP_KERNEL);
+               fmt2 = kzalloc(sizeof(*fmt2), GFP_KERNEL);
+               if (!fmt2) {
+                       err = -ENOMEM;
+                       break;
+               }
                memset(win,0,sizeof(*win));
-               memset(fmt2,0,sizeof(*fmt2));
 
                fmt2->type = V4L2_BUF_TYPE_VIDEO_OVERLAY;
                err = drv(inode, file, VIDIOC_G_FMT, fmt2);
@@ -461,8 +472,11 @@ v4l_compat_translate_ioctl(struct inode         *inode,
                struct video_window     *win = arg;
                int err1,err2;
 
-               fmt2 = kmalloc(sizeof(*fmt2),GFP_KERNEL);
-               memset(fmt2,0,sizeof(*fmt2));
+               fmt2 = kzalloc(sizeof(*fmt2), GFP_KERNEL);
+               if (!fmt2) {
+                       err = -ENOMEM;
+                       break;
+               }
                fmt2->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                drv(inode, file, VIDIOC_STREAMOFF, &fmt2->type);
                err1 = drv(inode, file, VIDIOC_G_FMT, fmt2);
@@ -584,6 +598,12 @@ v4l_compat_translate_ioctl(struct inode         *inode,
        {
                struct video_picture    *pict = arg;
 
+               fmt2 = kzalloc(sizeof(*fmt2), GFP_KERNEL);
+               if (!fmt2) {
+                       err = -ENOMEM;
+                       break;
+               }
+
                pict->brightness = get_v4l_control(inode, file,
                                                   V4L2_CID_BRIGHTNESS,drv);
                pict->hue = get_v4l_control(inode, file,
@@ -595,14 +615,16 @@ v4l_compat_translate_ioctl(struct inode         *inode,
                pict->whiteness = get_v4l_control(inode, file,
                                                  V4L2_CID_WHITENESS, drv);
 
-               fmt2 = kmalloc(sizeof(*fmt2),GFP_KERNEL);
-               memset(fmt2,0,sizeof(*fmt2));
                fmt2->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                err = drv(inode, file, VIDIOC_G_FMT, fmt2);
                if (err < 0) {
                        dprintk("VIDIOCGPICT / VIDIOC_G_FMT: %d\n",err);
                        break;
                }
+
+               pict->depth   = ((fmt2->fmt.pix.bytesperline<<3)
+                                + (fmt2->fmt.pix.width-1) )
+                                /fmt2->fmt.pix.width;
                pict->palette = pixelformat_to_palette(
                        fmt2->fmt.pix.pixelformat);
                break;
@@ -610,6 +632,14 @@ v4l_compat_translate_ioctl(struct inode         *inode,
        case VIDIOCSPICT: /*  set tone controls & partial capture format  */
        {
                struct video_picture    *pict = arg;
+               int mem_err = 0, ovl_err = 0;
+
+               fmt2 = kzalloc(sizeof(*fmt2), GFP_KERNEL);
+               if (!fmt2) {
+                       err = -ENOMEM;
+                       break;
+               }
+               memset(&fbuf2, 0, sizeof(fbuf2));
 
                set_v4l_control(inode, file,
                                V4L2_CID_BRIGHTNESS, pict->brightness, drv);
@@ -621,34 +651,58 @@ v4l_compat_translate_ioctl(struct inode         *inode,
                                V4L2_CID_SATURATION, pict->colour, drv);
                set_v4l_control(inode, file,
                                V4L2_CID_WHITENESS, pict->whiteness, drv);
+               /*
+                * V4L1 uses this ioctl to set both memory capture and overlay
+                * pixel format, while V4L2 has two different ioctls for this.
+                * Some cards may not support one or the other, and may support
+                * different pixel formats for memory vs overlay.
+                */
 
-               fmt2 = kmalloc(sizeof(*fmt2),GFP_KERNEL);
-               memset(fmt2,0,sizeof(*fmt2));
                fmt2->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                err = drv(inode, file, VIDIOC_G_FMT, fmt2);
-               if (err < 0)
+               /* If VIDIOC_G_FMT failed, then the driver likely doesn't
+                  support memory capture.  Trying to set the memory capture
+                  parameters would be pointless.  */
+               if (err < 0) {
                        dprintk("VIDIOCSPICT / VIDIOC_G_FMT: %d\n",err);
-               if (fmt2->fmt.pix.pixelformat !=
-                   palette_to_pixelformat(pict->palette)) {
+                       mem_err = -1000;  /* didn't even try */
+               } else if (fmt2->fmt.pix.pixelformat !=
+                        palette_to_pixelformat(pict->palette)) {
                        fmt2->fmt.pix.pixelformat = palette_to_pixelformat(
                                pict->palette);
-                       err = drv(inode, file, VIDIOC_S_FMT, fmt2);
-                       if (err < 0)
-                               dprintk("VIDIOCSPICT / VIDIOC_S_FMT: %d\n",err);
+                       mem_err = drv(inode, file, VIDIOC_S_FMT, fmt2);
+                       if (mem_err < 0)
+                               dprintk("VIDIOCSPICT / VIDIOC_S_FMT: %d\n",
+                                       mem_err);
                }
 
                err = drv(inode, file, VIDIOC_G_FBUF, &fbuf2);
-               if (err < 0)
+               /* If VIDIOC_G_FBUF failed, then the driver likely doesn't
+                  support overlay.  Trying to set the overlay parameters
+                  would be quite pointless.  */
+               if (err < 0) {
                        dprintk("VIDIOCSPICT / VIDIOC_G_FBUF: %d\n",err);
-               if (fbuf2.fmt.pixelformat !=
-                   palette_to_pixelformat(pict->palette)) {
+                       ovl_err = -1000;  /* didn't even try */
+               } else if (fbuf2.fmt.pixelformat !=
+                        palette_to_pixelformat(pict->palette)) {
                        fbuf2.fmt.pixelformat = palette_to_pixelformat(
                                pict->palette);
-                       err = drv(inode, file, VIDIOC_S_FBUF, &fbuf2);
-                       if (err < 0)
-                               dprintk("VIDIOCSPICT / VIDIOC_S_FBUF: %d\n",err);
-                       err = 0; /* likely fails for non-root */
+                       ovl_err = drv(inode, file, VIDIOC_S_FBUF, &fbuf2);
+                       if (ovl_err < 0)
+                               dprintk("VIDIOCSPICT / VIDIOC_S_FBUF: %d\n",
+                                       ovl_err);
                }
+               if (ovl_err < 0 && mem_err < 0)
+                       /* ioctl failed, couldn't set either parameter */
+                       if (mem_err != -1000) {
+                           err = mem_err;
+                       } else if (ovl_err == -EPERM) {
+                           err = 0;
+                       } else {
+                           err = ovl_err;
+                       }
+               else
+                       err = 0;
                break;
        }
        case VIDIOCGTUNER: /*  get tuner information  */
@@ -703,12 +757,22 @@ v4l_compat_translate_ioctl(struct inode         *inode,
        }
        case VIDIOCSTUNER: /*  select a tuner input  */
        {
-               err = 0;
+               struct video_tuner      *tun = arg;
+               struct v4l2_tuner       t;
+               memset(&t,0,sizeof(t));
+
+               t.index=tun->tuner;
+
+               err = drv(inode, file, VIDIOC_S_INPUT, &t);
+               if (err < 0)
+                       dprintk("VIDIOCSTUNER / VIDIOC_S_INPUT: %d\n",err);
+
                break;
        }
        case VIDIOCGFREQ: /*  get frequency  */
        {
                unsigned long *freq = arg;
+               memset(&freq2,0,sizeof(freq2));
 
                freq2.tuner = 0;
                err = drv(inode, file, VIDIOC_G_FREQUENCY, &freq2);
@@ -721,8 +785,8 @@ v4l_compat_translate_ioctl(struct inode         *inode,
        case VIDIOCSFREQ: /*  set frequency  */
        {
                unsigned long *freq = arg;
+               memset(&freq2,0,sizeof(freq2));
 
-               freq2.tuner = 0;
                drv(inode, file, VIDIOC_G_FREQUENCY, &freq2);
                freq2.frequency = *freq;
                err = drv(inode, file, VIDIOC_S_FREQUENCY, &freq2);
@@ -733,6 +797,7 @@ v4l_compat_translate_ioctl(struct inode         *inode,
        case VIDIOCGAUDIO: /*  get audio properties/controls  */
        {
                struct video_audio      *aud = arg;
+               memset(&aud2,0,sizeof(aud2));
 
                err = drv(inode, file, VIDIOC_G_AUDIO, &aud2);
                if (err < 0) {
@@ -846,9 +911,12 @@ v4l_compat_translate_ioctl(struct inode         *inode,
        {
                struct video_mmap       *mm = arg;
 
-               fmt2 = kmalloc(sizeof(*fmt2),GFP_KERNEL);
+               fmt2 = kzalloc(sizeof(*fmt2), GFP_KERNEL);
+               if (!fmt2) {
+                       err = -ENOMEM;
+                       break;
+               }
                memset(&buf2,0,sizeof(buf2));
-               memset(fmt2,0,sizeof(*fmt2));
 
                fmt2->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                err = drv(inode, file, VIDIOC_G_FMT, fmt2);
@@ -894,6 +962,7 @@ v4l_compat_translate_ioctl(struct inode         *inode,
        {
                int                     *i = arg;
 
+               memset(&buf2,0,sizeof(buf2));
                buf2.index = *i;
                buf2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                err = drv(inode, file, VIDIOC_QUERYBUF, &buf2);
@@ -942,8 +1011,11 @@ v4l_compat_translate_ioctl(struct inode         *inode,
        {
                struct vbi_format      *fmt = arg;
 
-               fmt2 = kmalloc(sizeof(*fmt2),GFP_KERNEL);
-               memset(fmt2, 0, sizeof(*fmt2));
+               fmt2 = kzalloc(sizeof(*fmt2), GFP_KERNEL);
+               if (!fmt2) {
+                       err = -ENOMEM;
+                       break;
+               }
                fmt2->type = V4L2_BUF_TYPE_VBI_CAPTURE;
 
                err = drv(inode, file, VIDIOC_G_FMT, fmt2);
@@ -951,6 +1023,10 @@ v4l_compat_translate_ioctl(struct inode         *inode,
                        dprintk("VIDIOCGVBIFMT / VIDIOC_G_FMT: %d\n", err);
                        break;
                }
+               if (fmt2->fmt.vbi.sample_format != V4L2_PIX_FMT_GREY) {
+                       err = -EINVAL;
+                       break;
+               }
                memset(fmt, 0, sizeof(*fmt));
                fmt->samples_per_line = fmt2->fmt.vbi.samples_per_line;
                fmt->sampling_rate    = fmt2->fmt.vbi.sampling_rate;
@@ -960,15 +1036,22 @@ v4l_compat_translate_ioctl(struct inode         *inode,
                fmt->start[1]         = fmt2->fmt.vbi.start[1];
                fmt->count[1]         = fmt2->fmt.vbi.count[1];
                fmt->flags            = fmt2->fmt.vbi.flags & 0x03;
-                break;
+               break;
        }
        case VIDIOCSVBIFMT:
        {
                struct vbi_format      *fmt = arg;
 
-               fmt2 = kmalloc(sizeof(*fmt2),GFP_KERNEL);
-               memset(fmt2, 0, sizeof(*fmt2));
+               if (VIDEO_PALETTE_RAW != fmt->sample_format) {
+                       err = -EINVAL;
+                       break;
+               }
 
+               fmt2 = kzalloc(sizeof(*fmt2), GFP_KERNEL);
+               if (!fmt2) {
+                       err = -ENOMEM;
+                       break;
+               }
                fmt2->type = V4L2_BUF_TYPE_VBI_CAPTURE;
                fmt2->fmt.vbi.samples_per_line = fmt->samples_per_line;
                fmt2->fmt.vbi.sampling_rate    = fmt->sampling_rate;
@@ -986,7 +1069,7 @@ v4l_compat_translate_ioctl(struct inode         *inode,
 
                if (fmt2->fmt.vbi.samples_per_line != fmt->samples_per_line ||
                    fmt2->fmt.vbi.sampling_rate    != fmt->sampling_rate    ||
-                   VIDEO_PALETTE_RAW              != fmt->sample_format    ||
+                   fmt2->fmt.vbi.sample_format    != V4L2_PIX_FMT_GREY     ||
                    fmt2->fmt.vbi.start[0]         != fmt->start[0]         ||
                    fmt2->fmt.vbi.count[0]         != fmt->count[0]         ||
                    fmt2->fmt.vbi.start[1]         != fmt->start[1]         ||