]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/media/video/gspca/gspca.c
V4L/DVB (9853): gspca: Webcam 093a:2622 added in pac7311.
[linux-2.6-omap-h63xx.git] / drivers / media / video / gspca / gspca.c
index 748a87e82e4423e2b9ecfb4c4ac621a339231e4a..a5c21ca959d8339c63cd7b576850f12f7226863b 100644 (file)
@@ -30,7 +30,6 @@
 #include <linux/string.h>
 #include <linux/pagemap.h>
 #include <linux/io.h>
-#include <linux/kref.h>
 #include <asm/page.h>
 #include <linux/uaccess.h>
 #include <linux/jiffies.h>
@@ -45,7 +44,7 @@ MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>");
 MODULE_DESCRIPTION("GSPCA USB Camera Driver");
 MODULE_LICENSE("GPL");
 
-#define DRIVER_VERSION_NUMBER  KERNEL_VERSION(2, 3, 0)
+#define DRIVER_VERSION_NUMBER  KERNEL_VERSION(2, 4, 0)
 
 static int video_nr = -1;
 
@@ -150,8 +149,11 @@ static void fill_frame(struct gspca_dev *gspca_dev,
 
                /* check the packet status and length */
                len = urb->iso_frame_desc[i].actual_length;
-               if (len == 0)
+               if (len == 0) {
+                       if (gspca_dev->empty_packet == 0)
+                               gspca_dev->empty_packet = 1;
                        continue;
+               }
                st = urb->iso_frame_desc[i].status;
                if (st) {
                        PDEBUG(D_ERR,
@@ -170,7 +172,6 @@ static void fill_frame(struct gspca_dev *gspca_dev,
        }
 
        /* resubmit the URB */
-       urb->status = 0;
        st = usb_submit_urb(urb, GFP_ATOMIC);
        if (st < 0)
                PDEBUG(D_ERR|D_PACK, "usb_submit_urb() ret %d", st);
@@ -200,11 +201,18 @@ static void bulk_irq(struct urb *urb
 {
        struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
        struct gspca_frame *frame;
+       int st;
 
        PDEBUG(D_PACK, "bulk irq");
        if (!gspca_dev->streaming)
                return;
-       if (urb->status != 0 && urb->status != -ECONNRESET) {
+       switch (urb->status) {
+       case 0:
+               break;
+       case -ECONNRESET:
+               urb->status = 0;
+               break;
+       default:
 #ifdef CONFIG_PM
                if (!gspca_dev->frozen)
 #endif
@@ -223,6 +231,13 @@ static void bulk_irq(struct urb *urb
                                        urb->transfer_buffer,
                                        urb->actual_length);
        }
+
+       /* resubmit the URB */
+       if (gspca_dev->cam.bulk_nurbs != 0) {
+               st = usb_submit_urb(urb, GFP_ATOMIC);
+               if (st < 0)
+                       PDEBUG(D_ERR|D_PACK, "usb_submit_urb() ret %d", st);
+       }
 }
 
 /*
@@ -285,7 +300,6 @@ struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev,
                frame->v4l2_buf.bytesused = frame->data_end - frame->data;
                frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_QUEUED;
                frame->v4l2_buf.flags |= V4L2_BUF_FLAG_DONE;
-               atomic_inc(&gspca_dev->nevent);
                wake_up_interruptible(&gspca_dev->wq);  /* event = new frame */
                i = (gspca_dev->fr_i + 1) % gspca_dev->nframes;
                gspca_dev->fr_i = i;
@@ -379,7 +393,6 @@ static int frame_alloc(struct gspca_dev *gspca_dev,
        gspca_dev->fr_i = gspca_dev->fr_o = gspca_dev->fr_q = 0;
        gspca_dev->last_packet_type = DISCARD_PACKET;
        gspca_dev->sequence = 0;
-       atomic_set(&gspca_dev->nevent, 0);
        return 0;
 }
 
@@ -520,11 +533,14 @@ static int create_urbs(struct gspca_dev *gspca_dev,
                nurbs = DEF_NURBS;
        } else {                                /* bulk */
                npkt = 0;
-               bsize = gspca_dev->cam. bulk_size;
+               bsize = gspca_dev->cam.bulk_size;
                if (bsize == 0)
                        bsize = psize;
                PDEBUG(D_STREAM, "bulk bsize:%d", bsize);
-               nurbs = 1;
+               if (gspca_dev->cam.bulk_nurbs != 0)
+                       nurbs = gspca_dev->cam.bulk_nurbs;
+               else
+                       nurbs = 1;
        }
 
        gspca_dev->nurbs = nurbs;
@@ -597,6 +613,12 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
                if (ret < 0)
                        goto out;
 
+               /* clear the bulk endpoint */
+               if (gspca_dev->alt == 0)        /* if bulk transfer */
+                       usb_clear_halt(gspca_dev->dev,
+                                       usb_rcvintpipe(gspca_dev->dev,
+                                                gspca_dev->cam.epaddr));
+
                /* start the cam */
                ret = gspca_dev->sd_desc->start(gspca_dev);
                if (ret < 0) {
@@ -604,10 +626,9 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
                        goto out;
                }
                gspca_dev->streaming = 1;
-               atomic_set(&gspca_dev->nevent, 0);
 
-               /* bulk transfers are started by the subdriver */
-               if (gspca_dev->alt == 0)
+               /* some bulk transfers are started by the subdriver */
+               if (gspca_dev->alt == 0 && gspca_dev->cam.bulk_nurbs == 0)
                        break;
 
                /* submit the URBs */
@@ -618,8 +639,11 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
                                        "usb_submit_urb [%d] err %d", n, ret);
                                gspca_dev->streaming = 0;
                                destroy_urbs(gspca_dev);
-                               if (ret == -ENOSPC)
+                               if (ret == -ENOSPC) {
+                                       msleep(20);     /* wait for kill
+                                                        * complete */
                                        break;  /* try the previous alt */
+                               }
                                goto out;
                        }
                }
@@ -637,7 +661,7 @@ static int gspca_set_alt0(struct gspca_dev *gspca_dev)
 
        ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0);
        if (ret < 0)
-               PDEBUG(D_ERR|D_STREAM, "set interface 0 err %d", ret);
+               PDEBUG(D_ERR|D_STREAM, "set alt 0 err %d", ret);
        return ret;
 }
 
@@ -645,7 +669,6 @@ static int gspca_set_alt0(struct gspca_dev *gspca_dev)
 static void gspca_stream_off(struct gspca_dev *gspca_dev)
 {
        gspca_dev->streaming = 0;
-       atomic_set(&gspca_dev->nevent, 0);
        if (gspca_dev->present
            && gspca_dev->sd_desc->stopN)
                gspca_dev->sd_desc->stopN(gspca_dev);
@@ -727,7 +750,7 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
                        if (fmtdesc->index == index)
                                break;          /* new format */
                        index++;
-                       if (index >= sizeof fmt_tb / sizeof fmt_tb[0])
+                       if (index >= ARRAY_SIZE(fmt_tb))
                                return -EINVAL;
                }
        }
@@ -846,11 +869,11 @@ out:
        return ret;
 }
 
-static void gspca_delete(struct kref *kref)
+static void gspca_release(struct video_device *vfd)
 {
-       struct gspca_dev *gspca_dev = container_of(kref, struct gspca_dev, kref);
+       struct gspca_dev *gspca_dev = container_of(vfd, struct gspca_dev, vdev);
 
-       PDEBUG(D_STREAM, "device deleted");
+       PDEBUG(D_STREAM, "device released");
 
        kfree(gspca_dev->usb_buf);
        kfree(gspca_dev);
@@ -862,7 +885,7 @@ static int dev_open(struct inode *inode, struct file *file)
        int ret;
 
        PDEBUG(D_STREAM, "%s open", current->comm);
-       gspca_dev = video_drvdata(file);
+       gspca_dev = (struct gspca_dev *) video_devdata(file);
        if (mutex_lock_interruptible(&gspca_dev->queue_lock))
                return -ERESTARTSYS;
        if (!gspca_dev->present) {
@@ -883,17 +906,14 @@ static int dev_open(struct inode *inode, struct file *file)
 
        gspca_dev->users++;
 
-       /* one more user */
-       kref_get(&gspca_dev->kref);
-
        file->private_data = gspca_dev;
 #ifdef GSPCA_DEBUG
        /* activate the v4l2 debug */
        if (gspca_debug & D_V4L2)
-               gspca_dev->vdev->debug |= V4L2_DEBUG_IOCTL
+               gspca_dev->vdev.debug |= V4L2_DEBUG_IOCTL
                                        | V4L2_DEBUG_IOCTL_ARG;
        else
-               gspca_dev->vdev->debug &= ~(V4L2_DEBUG_IOCTL
+               gspca_dev->vdev.debug &= ~(V4L2_DEBUG_IOCTL
                                        | V4L2_DEBUG_IOCTL_ARG);
 #endif
        ret = 0;
@@ -932,8 +952,6 @@ static int dev_close(struct inode *inode, struct file *file)
 
        PDEBUG(D_STREAM, "close done");
 
-       kref_put(&gspca_dev->kref, gspca_delete);
-
        return 0;
 }
 
@@ -1053,6 +1071,35 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
        return -EINVAL;
 }
 
+/*fixme: have an audio flag in gspca_dev?*/
+static int vidioc_s_audio(struct file *file, void *priv,
+                        struct v4l2_audio *audio)
+{
+       if (audio->index != 0)
+               return -EINVAL;
+       return 0;
+}
+
+static int vidioc_g_audio(struct file *file, void *priv,
+                        struct v4l2_audio *audio)
+{
+       memset(audio, 0, sizeof *audio);
+       strcpy(audio->name, "Microphone");
+       return 0;
+}
+
+static int vidioc_enumaudio(struct file *file, void *priv,
+                        struct v4l2_audio *audio)
+{
+       if (audio->index != 0)
+               return -EINVAL;
+
+       strcpy(audio->name, "Microphone");
+       audio->capability = 0;
+       audio->mode = 0;
+       return 0;
+}
+
 static int vidioc_querymenu(struct file *file, void *priv,
                            struct v4l2_querymenu *qmenu)
 {
@@ -1236,7 +1283,6 @@ static int vidioc_streamoff(struct file *file, void *priv,
        gspca_dev->fr_i = gspca_dev->fr_o = gspca_dev->fr_q = 0;
        gspca_dev->last_packet_type = DISCARD_PACKET;
        gspca_dev->sequence = 0;
-       atomic_set(&gspca_dev->nevent, 0);
        ret = 0;
 out:
        mutex_unlock(&gspca_dev->queue_lock);
@@ -1264,10 +1310,10 @@ static int vidioc_s_jpegcomp(struct file *file, void *priv,
        struct gspca_dev *gspca_dev = priv;
        int ret;
 
-       if (mutex_lock_interruptible(&gspca_dev->usb_lock))
-               return -ERESTARTSYS;
        if (!gspca_dev->sd_desc->set_jcomp)
                return -EINVAL;
+       if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+               return -ERESTARTSYS;
        ret = gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp);
        mutex_unlock(&gspca_dev->usb_lock);
        return ret;
@@ -1440,33 +1486,22 @@ static int frame_wait(struct gspca_dev *gspca_dev,
        i = gspca_dev->fr_o;
        j = gspca_dev->fr_queue[i];
        frame = &gspca_dev->frame[j];
-       if (frame->v4l2_buf.flags & V4L2_BUF_FLAG_DONE) {
-               atomic_dec(&gspca_dev->nevent);
-               goto ok;
-       }
-       if (nonblock_ing)                       /* no frame yet */
-               return -EAGAIN;
 
-       /* wait till a frame is ready */
-       for (;;) {
+       if (!(frame->v4l2_buf.flags & V4L2_BUF_FLAG_DONE)) {
+               if (nonblock_ing)
+                       return -EAGAIN;
+
+               /* wait till a frame is ready */
                ret = wait_event_interruptible_timeout(gspca_dev->wq,
-                                       atomic_read(&gspca_dev->nevent) > 0,
-                                       msecs_to_jiffies(3000));
-               if (ret <= 0) {
-                       if (ret < 0)
-                               return ret;     /* interrupt */
-                       return -EIO;            /* timeout */
-               }
-               atomic_dec(&gspca_dev->nevent);
-               if (!gspca_dev->streaming || !gspca_dev->present)
+                       (frame->v4l2_buf.flags & V4L2_BUF_FLAG_DONE) ||
+                       !gspca_dev->streaming || !gspca_dev->present,
+                       msecs_to_jiffies(3000));
+               if (ret < 0)
+                       return ret;
+               if (ret == 0 || !gspca_dev->streaming || !gspca_dev->present)
                        return -EIO;
-               i = gspca_dev->fr_o;
-               j = gspca_dev->fr_queue[i];
-               frame = &gspca_dev->frame[j];
-               if (frame->v4l2_buf.flags & V4L2_BUF_FLAG_DONE)
-                       break;
        }
-ok:
+
        gspca_dev->fr_o = (i + 1) % gspca_dev->nframes;
        PDEBUG(D_FRAM, "frame wait q:%d i:%d o:%d",
                gspca_dev->fr_q,
@@ -1781,6 +1816,9 @@ static const struct v4l2_ioctl_ops dev_ioctl_ops = {
        .vidioc_queryctrl       = vidioc_queryctrl,
        .vidioc_g_ctrl          = vidioc_g_ctrl,
        .vidioc_s_ctrl          = vidioc_s_ctrl,
+       .vidioc_g_audio         = vidioc_g_audio,
+       .vidioc_s_audio         = vidioc_s_audio,
+       .vidioc_enumaudio       = vidioc_enumaudio,
        .vidioc_querymenu       = vidioc_querymenu,
        .vidioc_enum_input      = vidioc_enum_input,
        .vidioc_g_input         = vidioc_g_input,
@@ -1802,7 +1840,7 @@ static struct video_device gspca_template = {
        .name = "gspca main driver",
        .fops = &dev_fops,
        .ioctl_ops = &dev_ioctl_ops,
-       .release = video_device_release,
+       .release = gspca_release,
        .minor = -1,
 };
 
@@ -1840,7 +1878,6 @@ int gspca_dev_probe(struct usb_interface *intf,
                err("couldn't kzalloc gspca struct");
                return -ENOMEM;
        }
-       kref_init(&gspca_dev->kref);
        gspca_dev->usb_buf = kmalloc(USB_BUF_SZ, GFP_KERNEL);
        if (!gspca_dev->usb_buf) {
                err("out of memory");
@@ -1852,12 +1889,13 @@ int gspca_dev_probe(struct usb_interface *intf,
        gspca_dev->nbalt = intf->num_altsetting;
        gspca_dev->sd_desc = sd_desc;
        gspca_dev->nbufread = 2;
+       gspca_dev->empty_packet = -1;   /* don't check the empty packets */
 
        /* configure the subdriver and initialize the USB device */
-       ret = gspca_dev->sd_desc->config(gspca_dev, id);
+       ret = sd_desc->config(gspca_dev, id);
        if (ret < 0)
                goto out;
-       ret = gspca_dev->sd_desc->init(gspca_dev);
+       ret = sd_desc->init(gspca_dev);
        if (ret < 0)
                goto out;
        ret = gspca_set_alt0(gspca_dev);
@@ -1871,18 +1909,15 @@ int gspca_dev_probe(struct usb_interface *intf,
        init_waitqueue_head(&gspca_dev->wq);
 
        /* init video stuff */
-       gspca_dev->vdev = video_device_alloc();
-       memcpy(gspca_dev->vdev, &gspca_template, sizeof gspca_template);
-       gspca_dev->vdev->parent = &dev->dev;
+       memcpy(&gspca_dev->vdev, &gspca_template, sizeof gspca_template);
+       gspca_dev->vdev.parent = &dev->dev;
        gspca_dev->module = module;
        gspca_dev->present = 1;
-       video_set_drvdata(gspca_dev->vdev, gspca_dev);
-       ret = video_register_device(gspca_dev->vdev,
+       ret = video_register_device(&gspca_dev->vdev,
                                  VFL_TYPE_GRABBER,
                                  video_nr);
        if (ret < 0) {
                err("video_register_device err %d", ret);
-               video_device_release(gspca_dev->vdev);
                goto out;
        }
 
@@ -1906,15 +1941,14 @@ void gspca_disconnect(struct usb_interface *intf)
 {
        struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
 
-       usb_set_intfdata(intf, NULL);
-
-/* We don't want people trying to open up the device */
-       video_unregister_device(gspca_dev->vdev);
-
        gspca_dev->present = 0;
        gspca_dev->streaming = 0;
 
-       kref_put(&gspca_dev->kref, gspca_delete);
+       usb_set_intfdata(intf, NULL);
+
+       /* release the device */
+       /* (this will call gspca_release() immediatly or on last close) */
+       video_unregister_device(&gspca_dev->vdev);
 
        PDEBUG(D_PROBE, "disconnect complete");
 }
@@ -1992,7 +2026,7 @@ int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum,
           desired lumination fast (with the risc of a slight overshoot) */
        steps = abs(desired_avg_lum - avg_lum) / deadzone;
 
-       PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d\n",
+       PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d",
                avg_lum, desired_avg_lum, steps);
 
        for (i = 0; i < steps; i++) {