Merge branch 'tmpreg' into devel
[safe/jmp/linux-2.6] / drivers / media / video / gspca / gspca.c
index 2358e6c..bd6214d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Main USB camera driver
  *
- * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ * Copyright (C) 2008-2009 Jean-Francois Moine (http://moinejf.free.fr)
  *
  * 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
 #include "gspca.h"
 
 /* global values */
-#define DEF_NURBS 2            /* default number of URBs */
+#define DEF_NURBS 3            /* default number of URBs */
+#if DEF_NURBS > MAX_NURBS
+#error "DEF_NURBS too big"
+#endif
 
 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, 4, 0)
-
-static int video_nr = -1;
+#define DRIVER_VERSION_NUMBER  KERNEL_VERSION(2, 8, 0)
 
 #ifdef GSPCA_DEBUG
 int gspca_debug = D_ERR | D_PROBE;
@@ -73,7 +74,7 @@ static void PDEBUG_MODE(char *txt, __u32 pixfmt, int w, int h)
 #define PDEBUG_MODE(txt, pixfmt, w, h)
 #endif
 
-/* specific memory types - !! should different from V4L2_MEMORY_xxx */
+/* specific memory types - !! should be different from V4L2_MEMORY_xxx */
 #define GSPCA_MEMORY_NO 0      /* V4L2_MEMORY_xxx starts from 1 */
 #define GSPCA_MEMORY_READ 7
 
@@ -98,7 +99,7 @@ static void gspca_vm_close(struct vm_area_struct *vma)
                frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_MAPPED;
 }
 
-static struct vm_operations_struct gspca_vm_ops = {
+static const struct vm_operations_struct gspca_vm_ops = {
        .open           = gspca_vm_open,
        .close          = gspca_vm_close,
 };
@@ -125,28 +126,24 @@ EXPORT_SYMBOL(gspca_get_i_frame);
 static void fill_frame(struct gspca_dev *gspca_dev,
                        struct urb *urb)
 {
-       struct gspca_frame *frame;
-       __u8 *data;             /* address of data in the iso message */
+       u8 *data;               /* address of data in the iso message */
        int i, len, st;
        cam_pkt_op pkt_scan;
 
        if (urb->status != 0) {
+               if (urb->status == -ESHUTDOWN)
+                       return;         /* disconnection */
 #ifdef CONFIG_PM
-               if (!gspca_dev->frozen)
+               if (gspca_dev->frozen)
+                       return;
 #endif
-                       PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
-               return;         /* disconnection ? */
+               PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
+               urb->status = 0;
+               goto resubmit;
        }
        pkt_scan = gspca_dev->sd_desc->pkt_scan;
        for (i = 0; i < urb->number_of_packets; i++) {
 
-               /* check the availability of the frame buffer */
-               frame = gspca_get_i_frame(gspca_dev);
-               if (!frame) {
-                       gspca_dev->last_packet_type = DISCARD_PACKET;
-                       break;
-               }
-
                /* check the packet status and length */
                len = urb->iso_frame_desc[i].actual_length;
                if (len == 0) {
@@ -166,11 +163,12 @@ static void fill_frame(struct gspca_dev *gspca_dev,
                /* let the packet be analyzed by the subdriver */
                PDEBUG(D_PACK, "packet [%d] o:%d l:%d",
                        i, urb->iso_frame_desc[i].offset, len);
-               data = (__u8 *) urb->transfer_buffer
+               data = (u8 *) urb->transfer_buffer
                                        + urb->iso_frame_desc[i].offset;
-               pkt_scan(gspca_dev, frame, data, len);
+               pkt_scan(gspca_dev, data, len);
        }
 
+resubmit:
        /* resubmit the URB */
        st = usb_submit_urb(urb, GFP_ATOMIC);
        if (st < 0)
@@ -182,8 +180,7 @@ static void fill_frame(struct gspca_dev *gspca_dev,
  *
  * Analyse each packet and call the subdriver for copy to the frame buffer.
  */
-static void isoc_irq(struct urb *urb
-)
+static void isoc_irq(struct urb *urb)
 {
        struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
 
@@ -196,11 +193,9 @@ static void isoc_irq(struct urb *urb
 /*
  * bulk message interrupt from the USB device
  */
-static void bulk_irq(struct urb *urb
-)
+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");
@@ -209,29 +204,24 @@ static void bulk_irq(struct urb *urb
        switch (urb->status) {
        case 0:
                break;
-       case -ECONNRESET:
-               urb->status = 0;
-               break;
+       case -ESHUTDOWN:
+               return;         /* disconnection */
        default:
 #ifdef CONFIG_PM
-               if (!gspca_dev->frozen)
+               if (gspca_dev->frozen)
+                       return;
 #endif
-                       PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
-               return;         /* disconnection ? */
+               PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
+               urb->status = 0;
+               goto resubmit;
        }
 
-       /* check the availability of the frame buffer */
-       frame = gspca_get_i_frame(gspca_dev);
-       if (!frame) {
-               gspca_dev->last_packet_type = DISCARD_PACKET;
-       } else {
-               PDEBUG(D_PACK, "packet l:%d", urb->actual_length);
-               gspca_dev->sd_desc->pkt_scan(gspca_dev,
-                                       frame,
-                                       urb->transfer_buffer,
-                                       urb->actual_length);
-       }
+       PDEBUG(D_PACK, "packet l:%d", urb->actual_length);
+       gspca_dev->sd_desc->pkt_scan(gspca_dev,
+                               urb->transfer_buffer,
+                               urb->actual_length);
 
+resubmit:
        /* resubmit the URB */
        if (gspca_dev->cam.bulk_nurbs != 0) {
                st = usb_submit_urb(urb, GFP_ATOMIC);
@@ -252,24 +242,27 @@ static void bulk_irq(struct urb *urb
  * DISCARD_PACKET invalidates the whole frame.
  * On LAST_PACKET, a new frame is returned.
  */
-struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev,
-                                   enum gspca_packet_type packet_type,
-                                   struct gspca_frame *frame,
-                                   const __u8 *data,
-                                   int len)
+void gspca_frame_add(struct gspca_dev *gspca_dev,
+                       enum gspca_packet_type packet_type,
+                       const u8 *data,
+                       int len)
 {
+       struct gspca_frame *frame;
        int i, j;
 
        PDEBUG(D_PACK, "add t:%d l:%d", packet_type, len);
 
+       /* check the availability of the frame buffer */
+       frame = gspca_dev->cur_frame;
+       if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS)
+                                       != V4L2_BUF_FLAG_QUEUED) {
+               gspca_dev->last_packet_type = DISCARD_PACKET;
+               return;
+       }
+
        /* when start of a new frame, if the current frame buffer
         * is not queued, discard the whole frame */
        if (packet_type == FIRST_PACKET) {
-               if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS)
-                                               != V4L2_BUF_FLAG_QUEUED) {
-                       gspca_dev->last_packet_type = DISCARD_PACKET;
-                       return frame;
-               }
                frame->data_end = frame->data;
                jiffies_to_timeval(get_jiffies_64(),
                                   &frame->v4l2_buf.timestamp);
@@ -277,7 +270,7 @@ struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev,
        } else if (gspca_dev->last_packet_type == DISCARD_PACKET) {
                if (packet_type == LAST_PACKET)
                        gspca_dev->last_packet_type = packet_type;
-               return frame;
+               return;
        }
 
        /* append the packet to the frame buffer */
@@ -309,9 +302,8 @@ struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev,
                        i,
                        gspca_dev->fr_o);
                j = gspca_dev->fr_queue[i];
-               frame = &gspca_dev->frame[j];
+               gspca_dev->cur_frame = &gspca_dev->frame[j];
        }
-       return frame;
 }
 EXPORT_SYMBOL(gspca_frame_add);
 
@@ -322,12 +314,13 @@ static int gspca_is_compressed(__u32 format)
        case V4L2_PIX_FMT_JPEG:
        case V4L2_PIX_FMT_SPCA561:
        case V4L2_PIX_FMT_PAC207:
+       case V4L2_PIX_FMT_MR97310A:
                return 1;
        }
        return 0;
 }
 
-static void *rvmalloc(unsigned long size)
+static void *rvmalloc(long size)
 {
        void *mem;
        unsigned long adr;
@@ -335,7 +328,7 @@ static void *rvmalloc(unsigned long size)
        mem = vmalloc_32(size);
        if (mem != NULL) {
                adr = (unsigned long) mem;
-               while ((long) size > 0) {
+               while (size > 0) {
                        SetPageReserved(vmalloc_to_page((void *) adr));
                        adr += PAGE_SIZE;
                        size -= PAGE_SIZE;
@@ -391,6 +384,7 @@ static int frame_alloc(struct gspca_dev *gspca_dev,
                frame->v4l2_buf.m.offset = i * frsz;
        }
        gspca_dev->fr_i = gspca_dev->fr_o = gspca_dev->fr_q = 0;
+       gspca_dev->cur_frame = &gspca_dev->frame[0];
        gspca_dev->last_packet_type = DISCARD_PACKET;
        gspca_dev->sequence = 0;
        return 0;
@@ -437,22 +431,17 @@ static void destroy_urbs(struct gspca_dev *gspca_dev)
  * look for an input transfer endpoint in an alternate setting
  */
 static struct usb_host_endpoint *alt_xfer(struct usb_host_interface *alt,
-                                         __u8 epaddr,
-                                         __u8 xfer)
+                                         int xfer)
 {
        struct usb_host_endpoint *ep;
        int i, attr;
 
-       epaddr |= USB_DIR_IN;
        for (i = 0; i < alt->desc.bNumEndpoints; i++) {
                ep = &alt->endpoint[i];
-               if (ep->desc.bEndpointAddress == epaddr) {
-                       attr = ep->desc.bmAttributes
-                                               & USB_ENDPOINT_XFERTYPE_MASK;
-                       if (attr == xfer)
-                               return ep;
-                       break;
-               }
+               attr = ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+               if (attr == xfer
+                   && ep->desc.wMaxPacketSize != 0)
+                       return ep;
        }
        return NULL;
 }
@@ -469,41 +458,40 @@ static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev)
 {
        struct usb_interface *intf;
        struct usb_host_endpoint *ep;
-       int i, ret;
+       int xfer, i, ret;
 
        intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
        ep = NULL;
+       xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK
+                                  : USB_ENDPOINT_XFER_ISOC;
        i = gspca_dev->alt;                     /* previous alt setting */
-
-       /* try isoc */
-       while (--i > 0) {                       /* alt 0 is unusable */
-               ep = alt_xfer(&intf->altsetting[i],
-                               gspca_dev->cam.epaddr,
-                               USB_ENDPOINT_XFER_ISOC);
-               if (ep)
-                       break;
+       if (gspca_dev->cam.reverse_alts) {
+               while (++i < gspca_dev->nbalt) {
+                       ep = alt_xfer(&intf->altsetting[i], xfer);
+                       if (ep)
+                               break;
+               }
+       } else {
+               while (--i >= 0) {
+                       ep = alt_xfer(&intf->altsetting[i], xfer);
+                       if (ep)
+                               break;
+               }
        }
-
-       /* if no isoc, try bulk */
        if (ep == NULL) {
-               ep = alt_xfer(&intf->altsetting[0],
-                               gspca_dev->cam.epaddr,
-                               USB_ENDPOINT_XFER_BULK);
-               if (ep == NULL) {
-                       err("no transfer endpoint found");
-                       return NULL;
-               }
+               err("no transfer endpoint found");
+               return NULL;
        }
        PDEBUG(D_STREAM, "use alt %d ep 0x%02x",
                        i, ep->desc.bEndpointAddress);
-       if (i > 0) {
+       gspca_dev->alt = i;             /* memorize the current alt setting */
+       if (gspca_dev->nbalt > 1) {
                ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, i);
                if (ret < 0) {
-                       err("set interface err %d", ret);
+                       err("set alt %d err %d", i, ret);
                        return NULL;
                }
        }
-       gspca_dev->alt = i;             /* memorize the current alt setting */
        return ep;
 }
 
@@ -519,13 +507,16 @@ static int create_urbs(struct gspca_dev *gspca_dev,
        /* calculate the packet size and the number of packets */
        psize = le16_to_cpu(ep->desc.wMaxPacketSize);
 
-       if (gspca_dev->alt != 0) {              /* isoc */
+       if (!gspca_dev->cam.bulk) {             /* isoc */
 
                /* See paragraph 5.9 / table 5-11 of the usb 2.0 spec. */
-               psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
-               npkt = ISO_MAX_SIZE / psize;
-               if (npkt > ISO_MAX_PKT)
-                       npkt = ISO_MAX_PKT;
+               if (gspca_dev->pkt_size == 0)
+                       psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
+               else
+                       psize = gspca_dev->pkt_size;
+               npkt = gspca_dev->cam.npkt;
+               if (npkt == 0)
+                       npkt = 32;              /* default value */
                bsize = psize * npkt;
                PDEBUG(D_STREAM,
                        "isoc %d pkts size %d = bsize:%d",
@@ -599,25 +590,38 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
        if (mutex_lock_interruptible(&gspca_dev->usb_lock))
                return -ERESTARTSYS;
 
+       if (!gspca_dev->present) {
+               ret = -ENODEV;
+               goto out;
+       }
+
        /* set the higher alternate setting and
         * loop until urb submit succeeds */
-       gspca_dev->alt = gspca_dev->nbalt;
+       if (gspca_dev->cam.reverse_alts)
+               gspca_dev->alt = 0;
+       else
+               gspca_dev->alt = gspca_dev->nbalt;
+
+       if (gspca_dev->sd_desc->isoc_init) {
+               ret = gspca_dev->sd_desc->isoc_init(gspca_dev);
+               if (ret < 0)
+                       goto out;
+       }
+       ep = get_ep(gspca_dev);
+       if (ep == NULL) {
+               ret = -EIO;
+               goto out;
+       }
        for (;;) {
                PDEBUG(D_STREAM, "init transfer alt %d", gspca_dev->alt);
-               ep = get_ep(gspca_dev);
-               if (ep == NULL) {
-                       ret = -EIO;
-                       goto out;
-               }
                ret = create_urbs(gspca_dev, ep);
                if (ret < 0)
                        goto out;
 
                /* clear the bulk endpoint */
-               if (gspca_dev->alt == 0)        /* if bulk transfer */
+               if (gspca_dev->cam.bulk)
                        usb_clear_halt(gspca_dev->dev,
-                                       usb_rcvintpipe(gspca_dev->dev,
-                                                gspca_dev->cam.epaddr));
+                                       gspca_dev->urb[0]->pipe);
 
                /* start the cam */
                ret = gspca_dev->sd_desc->start(gspca_dev);
@@ -628,24 +632,42 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
                gspca_dev->streaming = 1;
 
                /* some bulk transfers are started by the subdriver */
-               if (gspca_dev->alt == 0 && gspca_dev->cam.bulk_nurbs == 0)
+               if (gspca_dev->cam.bulk && gspca_dev->cam.bulk_nurbs == 0)
                        break;
 
                /* submit the URBs */
                for (n = 0; n < gspca_dev->nurbs; n++) {
                        ret = usb_submit_urb(gspca_dev->urb[n], GFP_KERNEL);
-                       if (ret < 0) {
-                               PDEBUG(D_ERR|D_STREAM,
-                                       "usb_submit_urb [%d] err %d", n, ret);
-                               gspca_dev->streaming = 0;
-                               destroy_urbs(gspca_dev);
-                               if (ret == -ENOSPC)
-                                       break;  /* try the previous alt */
-                               goto out;
-                       }
+                       if (ret < 0)
+                               break;
                }
                if (ret >= 0)
                        break;
+               gspca_dev->streaming = 0;
+               destroy_urbs(gspca_dev);
+               if (ret != -ENOSPC) {
+                       PDEBUG(D_ERR|D_STREAM,
+                               "usb_submit_urb alt %d err %d",
+                               gspca_dev->alt, ret);
+                       goto out;
+               }
+
+               /* the bandwidth is not wide enough
+                * negociate or try a lower alternate setting */
+               PDEBUG(D_ERR|D_STREAM,
+                       "bandwidth not wide enough - trying again");
+               msleep(20);     /* wait for kill complete */
+               if (gspca_dev->sd_desc->isoc_nego) {
+                       ret = gspca_dev->sd_desc->isoc_nego(gspca_dev);
+                       if (ret < 0)
+                               goto out;
+               } else {
+                       ep = get_ep(gspca_dev);
+                       if (ep == NULL) {
+                               ret = -EIO;
+                               goto out;
+                       }
+               }
        }
 out:
        mutex_unlock(&gspca_dev->usb_lock);
@@ -656,9 +678,11 @@ static int gspca_set_alt0(struct gspca_dev *gspca_dev)
 {
        int ret;
 
+       if (gspca_dev->alt == 0)
+               return 0;
        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;
 }
 
@@ -666,11 +690,14 @@ static int gspca_set_alt0(struct gspca_dev *gspca_dev)
 static void gspca_stream_off(struct gspca_dev *gspca_dev)
 {
        gspca_dev->streaming = 0;
-       if (gspca_dev->present
-           && gspca_dev->sd_desc->stopN)
-               gspca_dev->sd_desc->stopN(gspca_dev);
-       destroy_urbs(gspca_dev);
-       gspca_set_alt0(gspca_dev);
+       if (gspca_dev->present) {
+               if (gspca_dev->sd_desc->stopN)
+                       gspca_dev->sd_desc->stopN(gspca_dev);
+               destroy_urbs(gspca_dev);
+               gspca_set_alt0(gspca_dev);
+       }
+
+       /* always call stop0 to free the subdriver's resources */
        if (gspca_dev->sd_desc->stop0)
                gspca_dev->sd_desc->stop0(gspca_dev);
        PDEBUG(D_STREAM, "stream off OK");
@@ -725,6 +752,77 @@ static int gspca_get_mode(struct gspca_dev *gspca_dev,
        return -EINVAL;
 }
 
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vidioc_g_register(struct file *file, void *priv,
+                       struct v4l2_dbg_register *reg)
+{
+       int ret;
+       struct gspca_dev *gspca_dev = priv;
+
+       if (!gspca_dev->sd_desc->get_chip_ident)
+               return -EINVAL;
+
+       if (!gspca_dev->sd_desc->get_register)
+               return -EINVAL;
+
+       if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+               return -ERESTARTSYS;
+       gspca_dev->usb_err = 0;
+       if (gspca_dev->present)
+               ret = gspca_dev->sd_desc->get_register(gspca_dev, reg);
+       else
+               ret = -ENODEV;
+       mutex_unlock(&gspca_dev->usb_lock);
+
+       return ret;
+}
+
+static int vidioc_s_register(struct file *file, void *priv,
+                       struct v4l2_dbg_register *reg)
+{
+       int ret;
+       struct gspca_dev *gspca_dev = priv;
+
+       if (!gspca_dev->sd_desc->get_chip_ident)
+               return -EINVAL;
+
+       if (!gspca_dev->sd_desc->set_register)
+               return -EINVAL;
+
+       if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+               return -ERESTARTSYS;
+       gspca_dev->usb_err = 0;
+       if (gspca_dev->present)
+               ret = gspca_dev->sd_desc->set_register(gspca_dev, reg);
+       else
+               ret = -ENODEV;
+       mutex_unlock(&gspca_dev->usb_lock);
+
+       return ret;
+}
+#endif
+
+static int vidioc_g_chip_ident(struct file *file, void *priv,
+                       struct v4l2_dbg_chip_ident *chip)
+{
+       int ret;
+       struct gspca_dev *gspca_dev = priv;
+
+       if (!gspca_dev->sd_desc->get_chip_ident)
+               return -EINVAL;
+
+       if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+               return -ERESTARTSYS;
+       gspca_dev->usb_err = 0;
+       if (gspca_dev->present)
+               ret = gspca_dev->sd_desc->get_chip_ident(gspca_dev, chip);
+       else
+               ret = -ENODEV;
+       mutex_unlock(&gspca_dev->usb_lock);
+
+       return ret;
+}
+
 static int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
                                struct v4l2_fmtdesc *fmtdesc)
 {
@@ -757,7 +855,6 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
        fmtdesc->pixelformat = fmt_tb[index];
        if (gspca_is_compressed(fmt_tb[index]))
                fmtdesc->flags = V4L2_FMT_FLAG_COMPRESSED;
-       fmtdesc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        fmtdesc->description[0] = fmtdesc->pixelformat & 0xff;
        fmtdesc->description[1] = (fmtdesc->pixelformat >> 8) & 0xff;
        fmtdesc->description[2] = (fmtdesc->pixelformat >> 16) & 0xff;
@@ -772,8 +869,6 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
        struct gspca_dev *gspca_dev = priv;
        int mode;
 
-       if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-               return -EINVAL;
        mode = gspca_dev->curr_mode;
        memcpy(&fmt->fmt.pix, &gspca_dev->cam.cam_mode[mode],
                sizeof fmt->fmt.pix);
@@ -785,8 +880,6 @@ static int try_fmt_vid_cap(struct gspca_dev *gspca_dev,
 {
        int w, h, mode, mode2;
 
-       if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-               return -EINVAL;
        w = fmt->fmt.pix.width;
        h = fmt->fmt.pix.height;
 
@@ -866,22 +959,77 @@ out:
        return ret;
 }
 
+static int vidioc_enum_framesizes(struct file *file, void *priv,
+                                 struct v4l2_frmsizeenum *fsize)
+{
+       struct gspca_dev *gspca_dev = priv;
+       int i;
+       __u32 index = 0;
+
+       for (i = 0; i < gspca_dev->cam.nmodes; i++) {
+               if (fsize->pixel_format !=
+                               gspca_dev->cam.cam_mode[i].pixelformat)
+                       continue;
+
+               if (fsize->index == index) {
+                       fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+                       fsize->discrete.width =
+                               gspca_dev->cam.cam_mode[i].width;
+                       fsize->discrete.height =
+                               gspca_dev->cam.cam_mode[i].height;
+                       return 0;
+               }
+               index++;
+       }
+
+       return -EINVAL;
+}
+
+static int vidioc_enum_frameintervals(struct file *filp, void *priv,
+                                     struct v4l2_frmivalenum *fival)
+{
+       struct gspca_dev *gspca_dev = priv;
+       int mode = wxh_to_mode(gspca_dev, fival->width, fival->height);
+       __u32 i;
+
+       if (gspca_dev->cam.mode_framerates == NULL ||
+                       gspca_dev->cam.mode_framerates[mode].nrates == 0)
+               return -EINVAL;
+
+       if (fival->pixel_format !=
+                       gspca_dev->cam.cam_mode[mode].pixelformat)
+               return -EINVAL;
+
+       for (i = 0; i < gspca_dev->cam.mode_framerates[mode].nrates; i++) {
+               if (fival->index == i) {
+                       fival->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+                       fival->discrete.numerator = 1;
+                       fival->discrete.denominator =
+                               gspca_dev->cam.mode_framerates[mode].rates[i];
+                       return 0;
+               }
+       }
+
+       return -EINVAL;
+}
+
 static void gspca_release(struct video_device *vfd)
 {
        struct gspca_dev *gspca_dev = container_of(vfd, struct gspca_dev, vdev);
 
-       PDEBUG(D_STREAM, "device released");
+       PDEBUG(D_PROBE, "%s released",
+               video_device_node_name(&gspca_dev->vdev));
 
        kfree(gspca_dev->usb_buf);
        kfree(gspca_dev);
 }
 
-static int dev_open(struct inode *inode, struct file *file)
+static int dev_open(struct file *file)
 {
        struct gspca_dev *gspca_dev;
        int ret;
 
-       PDEBUG(D_STREAM, "%s open", current->comm);
+       PDEBUG(D_STREAM, "[%s] open", current->comm);
        gspca_dev = (struct gspca_dev *) video_devdata(file);
        if (mutex_lock_interruptible(&gspca_dev->queue_lock))
                return -ERESTARTSYS;
@@ -923,11 +1071,11 @@ out:
        return ret;
 }
 
-static int dev_close(struct inode *inode, struct file *file)
+static int dev_close(struct file *file)
 {
        struct gspca_dev *gspca_dev = file->private_data;
 
-       PDEBUG(D_STREAM, "%s close", current->comm);
+       PDEBUG(D_STREAM, "[%s] close", current->comm);
        if (mutex_lock_interruptible(&gspca_dev->queue_lock))
                return -ERESTARTSYS;
        gspca_dev->users--;
@@ -936,6 +1084,7 @@ static int dev_close(struct inode *inode, struct file *file)
        if (gspca_dev->capt_file == file) {
                if (gspca_dev->streaming) {
                        mutex_lock(&gspca_dev->usb_lock);
+                       gspca_dev->usb_err = 0;
                        gspca_stream_off(gspca_dev);
                        mutex_unlock(&gspca_dev->usb_lock);
                }
@@ -956,8 +1105,15 @@ static int vidioc_querycap(struct file *file, void  *priv,
                           struct v4l2_capability *cap)
 {
        struct gspca_dev *gspca_dev = priv;
+       int ret;
 
-       memset(cap, 0, sizeof *cap);
+       /* protect the access to the usb device */
+       if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+               return -ERESTARTSYS;
+       if (!gspca_dev->present) {
+               ret = -ENODEV;
+               goto out;
+       }
        strncpy(cap->driver, gspca_dev->sd_desc->name, sizeof cap->driver);
        if (gspca_dev->dev->product != NULL) {
                strncpy(cap->card, gspca_dev->dev->product,
@@ -968,52 +1124,68 @@ static int vidioc_querycap(struct file *file, void  *priv,
                        le16_to_cpu(gspca_dev->dev->descriptor.idVendor),
                        le16_to_cpu(gspca_dev->dev->descriptor.idProduct));
        }
-       strncpy(cap->bus_info, gspca_dev->dev->bus->bus_name,
-               sizeof cap->bus_info);
+       usb_make_path(gspca_dev->dev, cap->bus_info, sizeof(cap->bus_info));
        cap->version = DRIVER_VERSION_NUMBER;
        cap->capabilities = V4L2_CAP_VIDEO_CAPTURE
                          | V4L2_CAP_STREAMING
                          | V4L2_CAP_READWRITE;
-       return 0;
+       ret = 0;
+out:
+       mutex_unlock(&gspca_dev->usb_lock);
+       return ret;
+}
+
+static const struct ctrl *get_ctrl(struct gspca_dev *gspca_dev,
+                                  int id)
+{
+       const struct ctrl *ctrls;
+       int i;
+
+       for (i = 0, ctrls = gspca_dev->sd_desc->ctrls;
+            i < gspca_dev->sd_desc->nctrls;
+            i++, ctrls++) {
+               if (gspca_dev->ctrl_dis & (1 << i))
+                       continue;
+               if (id == ctrls->qctrl.id)
+                       return ctrls;
+       }
+       return NULL;
 }
 
 static int vidioc_queryctrl(struct file *file, void *priv,
                           struct v4l2_queryctrl *q_ctrl)
 {
        struct gspca_dev *gspca_dev = priv;
-       int i, ix;
+       const struct ctrl *ctrls;
+       int i;
        u32 id;
 
-       ix = -1;
+       ctrls = NULL;
        id = q_ctrl->id;
        if (id & V4L2_CTRL_FLAG_NEXT_CTRL) {
                id &= V4L2_CTRL_ID_MASK;
                id++;
                for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) {
-                       if (gspca_dev->sd_desc->ctrls[i].qctrl.id < id)
+                       if (gspca_dev->ctrl_dis & (1 << i))
                                continue;
-                       if (ix < 0) {
-                               ix = i;
+                       if (gspca_dev->sd_desc->ctrls[i].qctrl.id < id)
                                continue;
-                       }
-                       if (gspca_dev->sd_desc->ctrls[i].qctrl.id
-                                   > gspca_dev->sd_desc->ctrls[ix].qctrl.id)
+                       if (ctrls && gspca_dev->sd_desc->ctrls[i].qctrl.id
+                                           > ctrls->qctrl.id)
                                continue;
-                       ix = i;
-               }
-       }
-       for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) {
-               if (id == gspca_dev->sd_desc->ctrls[i].qctrl.id) {
-                       ix = i;
-                       break;
+                       ctrls = &gspca_dev->sd_desc->ctrls[i];
                }
+               if (ctrls == NULL)
+                       return -EINVAL;
+       } else {
+               ctrls = get_ctrl(gspca_dev, id);
+               if (ctrls == NULL)
+                       return -EINVAL;
+               i = ctrls - gspca_dev->sd_desc->ctrls;
        }
-       if (ix < 0)
-               return -EINVAL;
-       memcpy(q_ctrl, &gspca_dev->sd_desc->ctrls[ix].qctrl,
-               sizeof *q_ctrl);
-       if (gspca_dev->ctrl_dis & (1 << ix))
-               q_ctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+       memcpy(q_ctrl, ctrls, sizeof *q_ctrl);
+       if (gspca_dev->ctrl_inac & (1 << i))
+               q_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
        return 0;
 }
 
@@ -1022,50 +1194,75 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
 {
        struct gspca_dev *gspca_dev = priv;
        const struct ctrl *ctrls;
-       int i, ret;
+       int ret;
 
-       for (i = 0, ctrls = gspca_dev->sd_desc->ctrls;
-            i < gspca_dev->sd_desc->nctrls;
-            i++, ctrls++) {
-               if (ctrl->id != ctrls->qctrl.id)
-                       continue;
-               if (gspca_dev->ctrl_dis & (1 << i))
-                       return -EINVAL;
-               if (ctrl->value < ctrls->qctrl.minimum
-                   || ctrl->value > ctrls->qctrl.maximum)
-                       return -ERANGE;
-               PDEBUG(D_CONF, "set ctrl [%08x] = %d", ctrl->id, ctrl->value);
-               if (mutex_lock_interruptible(&gspca_dev->usb_lock))
-                       return -ERESTARTSYS;
+       ctrls = get_ctrl(gspca_dev, ctrl->id);
+       if (ctrls == NULL)
+               return -EINVAL;
+
+       if (ctrl->value < ctrls->qctrl.minimum
+           || ctrl->value > ctrls->qctrl.maximum)
+               return -ERANGE;
+       PDEBUG(D_CONF, "set ctrl [%08x] = %d", ctrl->id, ctrl->value);
+       if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+               return -ERESTARTSYS;
+       gspca_dev->usb_err = 0;
+       if (gspca_dev->present)
                ret = ctrls->set(gspca_dev, ctrl->value);
-               mutex_unlock(&gspca_dev->usb_lock);
-               return ret;
-       }
-       return -EINVAL;
+       else
+               ret = -ENODEV;
+       mutex_unlock(&gspca_dev->usb_lock);
+       return ret;
 }
 
 static int vidioc_g_ctrl(struct file *file, void *priv,
                         struct v4l2_control *ctrl)
 {
        struct gspca_dev *gspca_dev = priv;
-
        const struct ctrl *ctrls;
-       int i, ret;
+       int ret;
 
-       for (i = 0, ctrls = gspca_dev->sd_desc->ctrls;
-            i < gspca_dev->sd_desc->nctrls;
-            i++, ctrls++) {
-               if (ctrl->id != ctrls->qctrl.id)
-                       continue;
-               if (gspca_dev->ctrl_dis & (1 << i))
-                       return -EINVAL;
-               if (mutex_lock_interruptible(&gspca_dev->usb_lock))
-                       return -ERESTARTSYS;
+       ctrls = get_ctrl(gspca_dev, ctrl->id);
+       if (ctrls == NULL)
+               return -EINVAL;
+
+       if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+               return -ERESTARTSYS;
+       gspca_dev->usb_err = 0;
+       if (gspca_dev->present)
                ret = ctrls->get(gspca_dev, &ctrl->value);
-               mutex_unlock(&gspca_dev->usb_lock);
-               return ret;
-       }
-       return -EINVAL;
+       else
+               ret = -ENODEV;
+       mutex_unlock(&gspca_dev->usb_lock);
+       return ret;
+}
+
+/*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)
+{
+       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,
@@ -1085,8 +1282,8 @@ static int vidioc_enum_input(struct file *file, void *priv,
 
        if (input->index != 0)
                return -EINVAL;
-       memset(input, 0, sizeof *input);
        input->type = V4L2_INPUT_TYPE_CAMERA;
+       input->status = gspca_dev->cam.input_flags;
        strncpy(input->name, gspca_dev->sd_desc->name,
                sizeof input->name);
        return 0;
@@ -1111,8 +1308,6 @@ static int vidioc_reqbufs(struct file *file, void *priv,
        struct gspca_dev *gspca_dev = priv;
        int i, ret = 0;
 
-       if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-               return -EINVAL;
        switch (rb->memory) {
        case GSPCA_MEMORY_READ:                 /* (internal call) */
        case V4L2_MEMORY_MMAP:
@@ -1148,6 +1343,7 @@ static int vidioc_reqbufs(struct file *file, void *priv,
        /* stop streaming */
        if (gspca_dev->streaming) {
                mutex_lock(&gspca_dev->usb_lock);
+               gspca_dev->usb_err = 0;
                gspca_stream_off(gspca_dev);
                mutex_unlock(&gspca_dev->usb_lock);
        }
@@ -1177,8 +1373,7 @@ static int vidioc_querybuf(struct file *file, void *priv,
        struct gspca_dev *gspca_dev = priv;
        struct gspca_frame *frame;
 
-       if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE
-           || v4l2_buf->index < 0
+       if (v4l2_buf->index < 0
            || v4l2_buf->index >= gspca_dev->nframes)
                return -EINVAL;
 
@@ -1197,11 +1392,9 @@ static int vidioc_streamon(struct file *file, void *priv,
                return -EINVAL;
        if (mutex_lock_interruptible(&gspca_dev->queue_lock))
                return -ERESTARTSYS;
-       if (!gspca_dev->present) {
-               ret = -ENODEV;
-               goto out;
-       }
-       if (gspca_dev->nframes == 0) {
+
+       if (gspca_dev->nframes == 0
+           || !(gspca_dev->frame[0].v4l2_buf.flags & V4L2_BUF_FLAG_QUEUED)) {
                ret = -EINVAL;
                goto out;
        }
@@ -1242,6 +1435,7 @@ static int vidioc_streamoff(struct file *file, void *priv,
                ret = -ERESTARTSYS;
                goto out;
        }
+       gspca_dev->usb_err = 0;
        gspca_stream_off(gspca_dev);
        mutex_unlock(&gspca_dev->usb_lock);
 
@@ -1267,7 +1461,11 @@ static int vidioc_g_jpegcomp(struct file *file, void *priv,
                return -EINVAL;
        if (mutex_lock_interruptible(&gspca_dev->usb_lock))
                return -ERESTARTSYS;
-       ret = gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp);
+       gspca_dev->usb_err = 0;
+       if (gspca_dev->present)
+               ret = gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp);
+       else
+               ret = -ENODEV;
        mutex_unlock(&gspca_dev->usb_lock);
        return ret;
 }
@@ -1282,7 +1480,11 @@ static int vidioc_s_jpegcomp(struct file *file, void *priv,
                return -EINVAL;
        if (mutex_lock_interruptible(&gspca_dev->usb_lock))
                return -ERESTARTSYS;
-       ret = gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp);
+       gspca_dev->usb_err = 0;
+       if (gspca_dev->present)
+               ret = gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp);
+       else
+               ret = -ENODEV;
        mutex_unlock(&gspca_dev->usb_lock);
        return ret;
 }
@@ -1292,9 +1494,23 @@ static int vidioc_g_parm(struct file *filp, void *priv,
 {
        struct gspca_dev *gspca_dev = priv;
 
-       memset(parm, 0, sizeof *parm);
-       parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        parm->parm.capture.readbuffers = gspca_dev->nbufread;
+
+       if (gspca_dev->sd_desc->get_streamparm) {
+               int ret;
+
+               if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+                       return -ERESTARTSYS;
+               gspca_dev->usb_err = 0;
+               if (gspca_dev->present)
+                       ret = gspca_dev->sd_desc->get_streamparm(gspca_dev,
+                                                                parm);
+               else
+                       ret = -ENODEV;
+               mutex_unlock(&gspca_dev->usb_lock);
+               return ret;
+       }
+
        return 0;
 }
 
@@ -1309,12 +1525,22 @@ static int vidioc_s_parm(struct file *filp, void *priv,
                parm->parm.capture.readbuffers = gspca_dev->nbufread;
        else
                gspca_dev->nbufread = n;
-       return 0;
-}
 
-static int vidioc_s_std(struct file *filp, void *priv,
-                       v4l2_std_id *parm)
-{
+       if (gspca_dev->sd_desc->set_streamparm) {
+               int ret;
+
+               if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+                       return -ERESTARTSYS;
+               gspca_dev->usb_err = 0;
+               if (gspca_dev->present)
+                       ret = gspca_dev->sd_desc->set_streamparm(gspca_dev,
+                                                                parm);
+               else
+                       ret = -ENODEV;
+               mutex_unlock(&gspca_dev->usb_lock);
+               return ret;
+       }
+
        return 0;
 }
 
@@ -1332,7 +1558,6 @@ static int vidiocgmbuf(struct file *file, void *priv,
                {
                        struct v4l2_format fmt;
 
-                       memset(&fmt, 0, sizeof fmt);
                        fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                        i = gspca_dev->cam.nmodes - 1;  /* highest mode */
                        fmt.fmt.pix.width = gspca_dev->cam.cam_mode[i].width;
@@ -1478,7 +1703,9 @@ static int frame_wait(struct gspca_dev *gspca_dev,
 
        if (gspca_dev->sd_desc->dq_callback) {
                mutex_lock(&gspca_dev->usb_lock);
-               gspca_dev->sd_desc->dq_callback(gspca_dev);
+               gspca_dev->usb_err = 0;
+               if (gspca_dev->present)
+                       gspca_dev->sd_desc->dq_callback(gspca_dev);
                mutex_unlock(&gspca_dev->usb_lock);
        }
        return j;
@@ -1497,11 +1724,12 @@ static int vidioc_dqbuf(struct file *file, void *priv,
        int i, ret;
 
        PDEBUG(D_FRAM, "dqbuf");
-       if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-               return -EINVAL;
        if (v4l2_buf->memory != gspca_dev->memory)
                return -EINVAL;
 
+       if (!gspca_dev->present)
+               return -ENODEV;
+
        /* if not streaming, be sure the application will not loop forever */
        if (!(file->f_flags & O_NONBLOCK)
            && !gspca_dev->streaming && gspca_dev->users == 1)
@@ -1553,8 +1781,6 @@ static int vidioc_qbuf(struct file *file, void *priv,
        int i, index, ret;
 
        PDEBUG(D_FRAM, "qbuf %d", v4l2_buf->index);
-       if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-               return -EINVAL;
 
        if (mutex_lock_interruptible(&gspca_dev->queue_lock))
                return -ERESTARTSYS;
@@ -1589,6 +1815,8 @@ static int vidioc_qbuf(struct file *file, void *priv,
        /* put the buffer in the 'queued' queue */
        i = gspca_dev->fr_q;
        gspca_dev->fr_queue[i] = index;
+       if (gspca_dev->fr_i == i)
+               gspca_dev->cur_frame = frame;
        gspca_dev->fr_q = (i + 1) % gspca_dev->nframes;
        PDEBUG(D_FRAM, "qbuf q:%d i:%d o:%d",
                gspca_dev->fr_q,
@@ -1654,8 +1882,6 @@ static unsigned int dev_poll(struct file *file, poll_table *wait)
        PDEBUG(D_FRAM, "poll");
 
        poll_wait(file, &gspca_dev->wq, wait);
-       if (!gspca_dev->present)
-               return POLLERR;
 
        /* if reqbufs is not done, the user would use read() */
        if (gspca_dev->nframes == 0) {
@@ -1668,10 +1894,6 @@ static unsigned int dev_poll(struct file *file, poll_table *wait)
 
        if (mutex_lock_interruptible(&gspca_dev->queue_lock) != 0)
                return POLLERR;
-       if (!gspca_dev->present) {
-               ret = POLLERR;
-               goto out;
-       }
 
        /* check the next incoming buffer */
        i = gspca_dev->fr_o;
@@ -1680,8 +1902,9 @@ static unsigned int dev_poll(struct file *file, poll_table *wait)
                ret = POLLIN | POLLRDNORM;      /* something to read */
        else
                ret = 0;
-out:
        mutex_unlock(&gspca_dev->queue_lock);
+       if (!gspca_dev->present)
+               return POLLHUP;
        return ret;
 }
 
@@ -1758,17 +1981,13 @@ out:
        return ret;
 }
 
-static struct file_operations dev_fops = {
+static struct v4l2_file_operations dev_fops = {
        .owner = THIS_MODULE,
        .open = dev_open,
        .release = dev_close,
        .read = dev_read,
        .mmap = dev_mmap,
-       .ioctl = video_ioctl2,
-#ifdef CONFIG_COMPAT
-       .compat_ioctl = v4l_compat_ioctl32,
-#endif
-       .llseek = no_llseek,
+       .unlocked_ioctl = video_ioctl2,
        .poll   = dev_poll,
 };
 
@@ -1784,6 +2003,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,
@@ -1795,7 +2017,13 @@ static const struct v4l2_ioctl_ops dev_ioctl_ops = {
        .vidioc_s_jpegcomp      = vidioc_s_jpegcomp,
        .vidioc_g_parm          = vidioc_g_parm,
        .vidioc_s_parm          = vidioc_s_parm,
-       .vidioc_s_std           = vidioc_s_std,
+       .vidioc_enum_framesizes = vidioc_enum_framesizes,
+       .vidioc_enum_frameintervals = vidioc_enum_frameintervals,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       .vidioc_g_register      = vidioc_g_register,
+       .vidioc_s_register      = vidioc_s_register,
+#endif
+       .vidioc_g_chip_ident    = vidioc_g_chip_ident,
 #ifdef CONFIG_VIDEO_V4L1_COMPAT
        .vidiocgmbuf          = vidiocgmbuf,
 #endif
@@ -1806,7 +2034,6 @@ static struct video_device gspca_template = {
        .fops = &dev_fops,
        .ioctl_ops = &dev_ioctl_ops,
        .release = gspca_release,
-       .minor = -1,
 };
 
 /*
@@ -1829,11 +2056,15 @@ int gspca_dev_probe(struct usb_interface *intf,
        PDEBUG(D_PROBE, "probing %04x:%04x", id->idVendor, id->idProduct);
 
        /* we don't handle multi-config cameras */
-       if (dev->descriptor.bNumConfigurations != 1)
+       if (dev->descriptor.bNumConfigurations != 1) {
+               PDEBUG(D_ERR, "Too many config");
                return -ENODEV;
+       }
        interface = &intf->cur_altsetting->desc;
-       if (interface->bInterfaceNumber > 0)
+       if (interface->bInterfaceNumber > 0) {
+               PDEBUG(D_ERR, "intf != 0");
                return -ENODEV;
+       }
 
        /* create the device */
        if (dev_size < sizeof *gspca_dev)
@@ -1863,9 +2094,6 @@ int gspca_dev_probe(struct usb_interface *intf,
        ret = sd_desc->init(gspca_dev);
        if (ret < 0)
                goto out;
-       ret = gspca_set_alt0(gspca_dev);
-       if (ret < 0)
-               goto out;
        gspca_set_default_mode(gspca_dev);
 
        mutex_init(&gspca_dev->usb_lock);
@@ -1875,19 +2103,19 @@ int gspca_dev_probe(struct usb_interface *intf,
 
        /* init video stuff */
        memcpy(&gspca_dev->vdev, &gspca_template, sizeof gspca_template);
-       gspca_dev->vdev.parent = &dev->dev;
+       gspca_dev->vdev.parent = &intf->dev;
        gspca_dev->module = module;
        gspca_dev->present = 1;
        ret = video_register_device(&gspca_dev->vdev,
                                  VFL_TYPE_GRABBER,
-                                 video_nr);
+                                 -1);
        if (ret < 0) {
                err("video_register_device err %d", ret);
                goto out;
        }
 
        usb_set_intfdata(intf, gspca_dev);
-       PDEBUG(D_PROBE, "probe ok");
+       PDEBUG(D_PROBE, "%s created", video_device_node_name(&gspca_dev->vdev));
        return 0;
 out:
        kfree(gspca_dev->usb_buf);
@@ -1906,8 +2134,19 @@ void gspca_disconnect(struct usb_interface *intf)
 {
        struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
 
+       PDEBUG(D_PROBE, "%s disconnect",
+               video_device_node_name(&gspca_dev->vdev));
+       mutex_lock(&gspca_dev->usb_lock);
        gspca_dev->present = 0;
-       gspca_dev->streaming = 0;
+
+       if (gspca_dev->streaming) {
+               destroy_urbs(gspca_dev);
+               wake_up_interruptible(&gspca_dev->wq);
+       }
+
+       /* the device is freed at exit of this function */
+       gspca_dev->dev = NULL;
+       mutex_unlock(&gspca_dev->usb_lock);
 
        usb_set_intfdata(intf, NULL);
 
@@ -1915,7 +2154,7 @@ void gspca_disconnect(struct usb_interface *intf)
        /* (this will call gspca_release() immediatly or on last close) */
        video_unregister_device(&gspca_dev->vdev);
 
-       PDEBUG(D_PROBE, "disconnect complete");
+/*     PDEBUG(D_PROBE, "disconnect complete"); */
 }
 EXPORT_SYMBOL(gspca_disconnect);