V4L/DVB (6271): V4L: Add basic support for suspend/resume for saa7134
[safe/jmp/linux-2.6] / drivers / media / video / stv680.c
index 9636da2..4dc5bc7 100644 (file)
@@ -1,16 +1,16 @@
 /*
  *  STV0680 USB Camera Driver, by Kevin Sisson (kjsisson@bellsouth.net)
- *  
- * Thanks to STMicroelectronics for information on the usb commands, and 
- * to Steve Miller at STM for his help and encouragement while I was 
+ *
+ * Thanks to STMicroelectronics for information on the usb commands, and
+ * to Steve Miller at STM for his help and encouragement while I was
  * writing this driver.
  *
- * This driver is based heavily on the 
+ * This driver is based heavily on the
  * Endpoints (formerly known as AOX) se401 USB Camera Driver
  * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.org)
  *
  * Still somewhat based on the Linux ov511 driver.
- * 
+ *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
  * Free Software Foundation; either version 2 of the License, or (at your
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * History: 
- * ver 0.1 October, 2001. Initial attempt. 
+ * History:
+ * ver 0.1 October, 2001. Initial attempt.
  *
  * ver 0.2 November, 2001. Fixed asbility to resize, added brightness
  *                         function, made more stable (?)
  *
- * ver 0.21 Nov, 2001.     Added gamma correction and white balance, 
- *                         due to Alexander Schwartz. Still trying to 
+ * ver 0.21 Nov, 2001.     Added gamma correction and white balance,
+ *                         due to Alexander Schwartz. Still trying to
  *                         improve stablility. Moved stuff into stv680.h
  *
- * ver 0.22 Nov, 2001.    Added sharpen function (by Michael Sweet, 
- *                         mike@easysw.com) from GIMP, also used in pencam. 
+ * ver 0.22 Nov, 2001.    Added sharpen function (by Michael Sweet,
+ *                         mike@easysw.com) from GIMP, also used in pencam.
  *                         Simple, fast, good integer math routine.
  *
  * ver 0.23 Dec, 2001 (gkh)
  *                        Lindent, and did other minor tweaks to get
  *                        things to work properly with 2.5.1
  *
- * ver 0.24 Jan, 2002 (kjs) 
+ * ver 0.24 Jan, 2002 (kjs)
  *                         Fixed the problem with webcam crashing after
- *                         two pictures. Changed the way pic is halved to 
- *                         improve quality. Got rid of green line around 
- *                         frame. Fix brightness reset when changing size 
+ *                         two pictures. Changed the way pic is halved to
+ *                         improve quality. Got rid of green line around
+ *                         frame. Fix brightness reset when changing size
  *                         bug. Adjusted gamma filters slightly.
  *
  * ver 0.25 Jan, 2002 (kjs)
@@ -58,7 +58,6 @@
  *                        Fixed proc entry removal bug.
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/vmalloc.h>
@@ -66,6 +65,7 @@
 #include <linux/pagemap.h>
 #include <linux/errno.h>
 #include <linux/videodev.h>
+#include <media/v4l2-common.h>
 #include <linux/usb.h>
 #include <linux/mutex.h>
 
@@ -484,7 +484,7 @@ exit:
                PDEBUG (1, "STV(i): swapRGB is (forced) ON");
        else if (swapRGB_on == -1)
                PDEBUG (1, "STV(i): swapRGB is (forced) OFF");
-       
+
        if (stv_set_video_mode (stv680) < 0) {
                PDEBUG (0, "STV(e): Could not set video mode in stv_init");
                return -1;
@@ -516,16 +516,45 @@ stv680_file(frames_read, framecount, "%d\n");
 stv680_file(packets_dropped, dropped, "%d\n");
 stv680_file(decoding_errors, error, "%d\n");
 
-static void stv680_create_sysfs_files(struct video_device *vdev)
+static int stv680_create_sysfs_files(struct video_device *vdev)
 {
-       video_device_create_file(vdev, &class_device_attr_model);
-       video_device_create_file(vdev, &class_device_attr_in_use);
-       video_device_create_file(vdev, &class_device_attr_streaming);
-       video_device_create_file(vdev, &class_device_attr_palette);
-       video_device_create_file(vdev, &class_device_attr_frames_total);
-       video_device_create_file(vdev, &class_device_attr_frames_read);
-       video_device_create_file(vdev, &class_device_attr_packets_dropped);
-       video_device_create_file(vdev, &class_device_attr_decoding_errors);
+       int rc;
+
+       rc = video_device_create_file(vdev, &class_device_attr_model);
+       if (rc) goto err;
+       rc = video_device_create_file(vdev, &class_device_attr_in_use);
+       if (rc) goto err_model;
+       rc = video_device_create_file(vdev, &class_device_attr_streaming);
+       if (rc) goto err_inuse;
+       rc = video_device_create_file(vdev, &class_device_attr_palette);
+       if (rc) goto err_stream;
+       rc = video_device_create_file(vdev, &class_device_attr_frames_total);
+       if (rc) goto err_pal;
+       rc = video_device_create_file(vdev, &class_device_attr_frames_read);
+       if (rc) goto err_framtot;
+       rc = video_device_create_file(vdev, &class_device_attr_packets_dropped);
+       if (rc) goto err_framread;
+       rc = video_device_create_file(vdev, &class_device_attr_decoding_errors);
+       if (rc) goto err_dropped;
+
+       return 0;
+
+err_dropped:
+       video_device_remove_file(vdev, &class_device_attr_packets_dropped);
+err_framread:
+       video_device_remove_file(vdev, &class_device_attr_frames_read);
+err_framtot:
+       video_device_remove_file(vdev, &class_device_attr_frames_total);
+err_pal:
+       video_device_remove_file(vdev, &class_device_attr_palette);
+err_stream:
+       video_device_remove_file(vdev, &class_device_attr_streaming);
+err_inuse:
+       video_device_remove_file(vdev, &class_device_attr_in_use);
+err_model:
+       video_device_remove_file(vdev, &class_device_attr_model);
+err:
+       return rc;
 }
 
 static void stv680_remove_sysfs_files(struct video_device *vdev)
@@ -570,7 +599,7 @@ static int stv680_set_pict (struct usb_stv *stv680, struct video_picture *p)
        if (stv680->brightness != p->brightness) {
                stv680->chgbright = 1;
                stv680->brightness = p->brightness;
-       } 
+       }
 
        stv680->whiteness = p->whiteness;       /* greyscale */
        stv680->colour = p->colour;
@@ -582,7 +611,7 @@ static int stv680_set_pict (struct usb_stv *stv680, struct video_picture *p)
        return 0;
 }
 
-static void stv680_video_irq (struct urb *urb, struct pt_regs *regs)
+static void stv680_video_irq (struct urb *urb)
 {
        struct usb_stv *stv680 = urb->context;
        int length = urb->actual_length;
@@ -612,7 +641,7 @@ static void stv680_video_irq (struct urb *urb, struct pt_regs *regs)
 
                case BUFFER_UNUSED:
                        memcpy (stv680->scratch[stv680->scratch_next].data,
-                               (unsigned char *) urb->transfer_buffer, length);
+                               (unsigned char *) urb->transfer_buffer, length);
                        stv680->scratch[stv680->scratch_next].state = BUFFER_READY;
                        stv680->scratch[stv680->scratch_next].length = length;
                        if (waitqueue_active (&stv680->wq)) {
@@ -658,7 +687,7 @@ static int stv680_start_stream (struct usb_stv *stv680)
                stv680->sbuf[i].data = kmalloc (stv680->rawbufsize, GFP_KERNEL);
                if (stv680->sbuf[i].data == NULL) {
                        PDEBUG (0, "STV(e): Could not kmalloc raw data buffer %i", i);
-                       return -1;
+                       goto nomem_err;
                }
        }
 
@@ -669,7 +698,7 @@ static int stv680_start_stream (struct usb_stv *stv680)
                stv680->scratch[i].data = kmalloc (stv680->rawbufsize, GFP_KERNEL);
                if (stv680->scratch[i].data == NULL) {
                        PDEBUG (0, "STV(e): Could not kmalloc raw scratch buffer %i", i);
-                       return -1;
+                       goto nomem_err;
                }
                stv680->scratch[i].state = BUFFER_UNUSED;
        }
@@ -677,7 +706,7 @@ static int stv680_start_stream (struct usb_stv *stv680)
        for (i = 0; i < STV680_NUMSBUF; i++) {
                urb = usb_alloc_urb (0, GFP_KERNEL);
                if (!urb)
-                       return -ENOMEM;
+                       goto nomem_err;
 
                /* sbuf is urb->transfer_buffer, later gets memcpyed to scratch */
                usb_fill_bulk_urb (urb, stv680->udev,
@@ -686,12 +715,30 @@ static int stv680_start_stream (struct usb_stv *stv680)
                                   stv680_video_irq, stv680);
                stv680->urb[i] = urb;
                err = usb_submit_urb (stv680->urb[i], GFP_KERNEL);
-               if (err)
-                       PDEBUG (0, "STV(e): urb burned down in start stream");
+               if (err) {
+                       PDEBUG (0, "STV(e): urb burned down with err "
+                                  "%d in start stream %d", err, i);
+                       goto nomem_err;
+               }
        }                       /* i STV680_NUMSBUF */
 
        stv680->framecount = 0;
        return 0;
+
+ nomem_err:
+       for (i = 0; i < STV680_NUMSCRATCH; i++) {
+               kfree(stv680->scratch[i].data);
+               stv680->scratch[i].data = NULL;
+       }
+       for (i = 0; i < STV680_NUMSBUF; i++) {
+               usb_kill_urb(stv680->urb[i]);
+               usb_free_urb(stv680->urb[i]);
+               stv680->urb[i] = NULL;
+               kfree(stv680->sbuf[i].data);
+               stv680->sbuf[i].data = NULL;
+       }
+       return -ENOMEM;
+
 }
 
 static int stv680_stop_stream (struct usb_stv *stv680)
@@ -752,7 +799,7 @@ static int stv680_set_size (struct usb_stv *stv680, int width, int height)
                PDEBUG (1, "STV(e): request for non-supported size: request: v.width = %i, v.height = %i  actual: stv.width = %i, stv.height = %i", width, height, stv680->vwidth, stv680->vheight);
                return 1;
        }
-       
+
        /* Stop a current stream and start it again at the new size */
        if (wasstreaming)
                stv680_stop_stream (stv680);
@@ -773,7 +820,7 @@ static int stv680_set_size (struct usb_stv *stv680, int width, int height)
 
 /*
  * STV0680 Vision Camera Chipset Driver
- * Copyright (C) 2000 Adam Harrison <adam@antispin.org> 
+ * Copyright (C) 2000 Adam Harrison <adam@antispin.org>
 */
 
 #define RED 0
@@ -842,7 +889,7 @@ static void bayer_unshuffle (struct usb_stv *stv680, struct stv680_scratch *buff
                                colour = 2;
                                break;
                        }
-                       i = (y * vw + x) * 3;   
+                       i = (y * vw + x) * 3;
                        *(output + i + colour) = (unsigned char) p;
                }               /* for x */
 
@@ -850,9 +897,9 @@ static void bayer_unshuffle (struct usb_stv *stv680, struct stv680_scratch *buff
 
        /****** gamma correction plus hardcoded white balance */
        /* Thanks to Alexander Schwartx <alexander.schwartx@gmx.net> for this code.
-          Correction values red[], green[], blue[], are generated by 
-          (pow(i/256.0, GAMMA)*255.0)*white balanceRGB where GAMMA=0.55, 1<i<255. 
-          White balance (RGB)= 1.0, 1.17, 1.48. Values are calculated as double float and 
+          Correction values red[], green[], blue[], are generated by
+          (pow(i/256.0, GAMMA)*255.0)*white balanceRGB where GAMMA=0.55, 1<i<255.
+          White balance (RGB)= 1.0, 1.17, 1.48. Values are calculated as double float and
           converted to unsigned char. Values are in stv680.h  */
 
        for (y = 0; y < vh; y++) {
@@ -1336,7 +1383,7 @@ static ssize_t stv680_read (struct file *file, char __user *buf,
        return realcount;
 }                              /* stv680_read */
 
-static struct file_operations stv680_fops = {
+static const struct file_operations stv680_fops = {
        .owner =        THIS_MODULE,
        .open =         stv_open,
        .release =      stv_close,
@@ -1418,9 +1465,13 @@ static int stv680_probe (struct usb_interface *intf, const struct usb_device_id
        PDEBUG (0, "STV(i): registered new video device: video%d", stv680->vdev->minor);
 
        usb_set_intfdata (intf, stv680);
-       stv680_create_sysfs_files(stv680->vdev);
+       retval = stv680_create_sysfs_files(stv680->vdev);
+       if (retval)
+               goto error_unreg;
        return 0;
 
+error_unreg:
+       video_unregister_device(stv680->vdev);
 error_vdev:
        video_device_release(stv680->vdev);
 error: