]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/media/video/gspca/gspca.c
Merge current mainline tree into linux-omap tree
[linux-2.6-omap-h63xx.git] / drivers / media / video / gspca / gspca.c
index 15d302b28b795d7d5e727a1e1091964dbb1e651f..ac95c55887df4b38eb1cd8c4478538c53e042aef 100644 (file)
@@ -114,7 +114,10 @@ static void fill_frame(struct gspca_dev *gspca_dev,
        cam_pkt_op pkt_scan;
 
        if (urb->status != 0) {
-               PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
+#ifdef CONFIG_PM
+               if (!gspca_dev->frozen)
+#endif
+                       PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
                return;         /* disconnection ? */
        }
        pkt_scan = gspca_dev->sd_desc->pkt_scan;
@@ -456,6 +459,7 @@ static int create_urbs(struct gspca_dev *gspca_dev,
                urb = usb_alloc_urb(npkt, GFP_KERNEL);
                if (!urb) {
                        err("usb_alloc_urb failed");
+                       destroy_urbs(gspca_dev);
                        return -ENOMEM;
                }
                urb->transfer_buffer = usb_buffer_alloc(gspca_dev->dev,
@@ -465,8 +469,8 @@ static int create_urbs(struct gspca_dev *gspca_dev,
 
                if (urb->transfer_buffer == NULL) {
                        usb_free_urb(urb);
-                       destroy_urbs(gspca_dev);
                        err("usb_buffer_urb failed");
+                       destroy_urbs(gspca_dev);
                        return -ENOMEM;
                }
                gspca_dev->urb[n] = urb;
@@ -555,10 +559,12 @@ 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);
+               if (gspca_dev->sd_desc->stopN)
+                       gspca_dev->sd_desc->stopN(gspca_dev);
                destroy_urbs(gspca_dev);
                gspca_set_alt0(gspca_dev);
-               gspca_dev->sd_desc->stop0(gspca_dev);
+               if (gspca_dev->sd_desc->stop0)
+                       gspca_dev->sd_desc->stop0(gspca_dev);
                PDEBUG(D_STREAM, "stream off OK");
        }
 }
@@ -767,19 +773,7 @@ static int dev_open(struct inode *inode, struct file *file)
                goto out;
        }
 
-       /* if not done yet, initialize the sensor */
-       if (gspca_dev->users == 0) {
-               if (mutex_lock_interruptible(&gspca_dev->usb_lock)) {
-                       ret = -ERESTARTSYS;
-                       goto out;
-               }
-               ret = gspca_dev->sd_desc->open(gspca_dev);
-               mutex_unlock(&gspca_dev->usb_lock);
-               if (ret != 0) {
-                       PDEBUG(D_ERR|D_CONF, "init device failed %d", ret);
-                       goto out;
-               }
-       } else if (gspca_dev->users > 4) {      /* (arbitrary value) */
+       if (gspca_dev->users > 4) {     /* (arbitrary value) */
                ret = -EBUSY;
                goto out;
        }
@@ -792,6 +786,7 @@ static int dev_open(struct inode *inode, struct file *file)
        else
                gspca_dev->vdev.debug &= ~3;
 #endif
+       ret = 0;
 out:
        mutex_unlock(&gspca_dev->queue_lock);
        if (ret != 0)
@@ -812,11 +807,11 @@ static int dev_close(struct inode *inode, struct file *file)
 
        /* if the file did the capture, free the streaming resources */
        if (gspca_dev->capt_file == file) {
-               mutex_lock(&gspca_dev->usb_lock);
-               if (gspca_dev->streaming)
+               if (gspca_dev->streaming) {
+                       mutex_lock(&gspca_dev->usb_lock);
                        gspca_stream_off(gspca_dev);
-               gspca_dev->sd_desc->close(gspca_dev);
-               mutex_unlock(&gspca_dev->usb_lock);
+                       mutex_unlock(&gspca_dev->usb_lock);
+               }
                frame_free(gspca_dev);
                gspca_dev->capt_file = NULL;
                gspca_dev->memory = GSPCA_MEMORY_NO;
@@ -853,42 +848,44 @@ static int vidioc_querycap(struct file *file, void  *priv,
        return 0;
 }
 
-/* the use of V4L2_CTRL_FLAG_NEXT_CTRL asks for the controls to be sorted */
 static int vidioc_queryctrl(struct file *file, void *priv,
                           struct v4l2_queryctrl *q_ctrl)
 {
        struct gspca_dev *gspca_dev = priv;
-       int i;
+       int i, ix;
        u32 id;
 
+       ix = -1;
        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 (id >= gspca_dev->sd_desc->ctrls[i].qctrl.id) {
-                               memcpy(q_ctrl,
-                                       &gspca_dev->sd_desc->ctrls[i].qctrl,
-                                       sizeof *q_ctrl);
-                               return 0;
+                       if (gspca_dev->sd_desc->ctrls[i].qctrl.id < id)
+                               continue;
+                       if (ix < 0) {
+                               ix = i;
+                               continue;
                        }
+                       if (gspca_dev->sd_desc->ctrls[i].qctrl.id
+                                   > gspca_dev->sd_desc->ctrls[ix].qctrl.id)
+                               continue;
+                       ix = i;
                }
-               return -EINVAL;
        }
        for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) {
                if (id == gspca_dev->sd_desc->ctrls[i].qctrl.id) {
-                       memcpy(q_ctrl,
-                               &gspca_dev->sd_desc->ctrls[i].qctrl,
-                               sizeof *q_ctrl);
-                       return 0;
+                       ix = i;
+                       break;
                }
        }
-       if (id >= V4L2_CID_BASE
-           && id <= V4L2_CID_LASTP1) {
+       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;
-               return 0;
-       }
-       return -EINVAL;
+       return 0;
 }
 
 static int vidioc_s_ctrl(struct file *file, void *priv,
@@ -903,6 +900,8 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
             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;
@@ -929,6 +928,8 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
             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;
                ret = ctrls->get(gspca_dev, &ctrl->value);
@@ -1403,7 +1404,7 @@ static int vidioc_dqbuf(struct file *file, void *priv,
        i = ret;                                /* frame index */
        frame = &gspca_dev->frame[i];
        if (gspca_dev->memory == V4L2_MEMORY_USERPTR) {
-               if (copy_to_user((__u8 *) frame->v4l2_buf.m.userptr,
+               if (copy_to_user((__u8 __user *) frame->v4l2_buf.m.userptr,
                                 frame->data,
                                 frame->v4l2_buf.bytesused)) {
                        PDEBUG(D_ERR|D_STREAM,
@@ -1731,6 +1732,12 @@ int gspca_dev_probe(struct usb_interface *intf,
                err("couldn't kzalloc gspca struct");
                return -EIO;
        }
+       gspca_dev->usb_buf = kmalloc(USB_BUF_SZ, GFP_KERNEL);
+       if (!gspca_dev->usb_buf) {
+               err("out of memory");
+               ret = -EIO;
+               goto out;
+       }
        gspca_dev->dev = dev;
        gspca_dev->iface = interface->bInterfaceNumber;
        gspca_dev->nbalt = intf->num_altsetting;
@@ -1738,8 +1745,11 @@ int gspca_dev_probe(struct usb_interface *intf,
 /*     gspca_dev->users = 0;                   (done by kzalloc) */
        gspca_dev->nbufread = 2;
 
-       /* configure the subdriver */
+       /* configure the subdriver and initialize the USB device */
        ret = gspca_dev->sd_desc->config(gspca_dev, id);
+       if (ret < 0)
+               goto out;
+       ret = gspca_dev->sd_desc->init(gspca_dev);
        if (ret < 0)
                goto out;
        ret = gspca_set_alt0(gspca_dev);
@@ -1771,6 +1781,7 @@ int gspca_dev_probe(struct usb_interface *intf,
        PDEBUG(D_PROBE, "probe ok");
        return 0;
 out:
+       kfree(gspca_dev->usb_buf);
        kfree(gspca_dev);
        return ret;
 }
@@ -1803,11 +1814,42 @@ void gspca_disconnect(struct usb_interface *intf)
 /* We don't want people trying to open up the device */
        video_unregister_device(&gspca_dev->vdev);
 /* Free the memory */
+       kfree(gspca_dev->usb_buf);
        kfree(gspca_dev);
        PDEBUG(D_PROBE, "disconnect complete");
 }
 EXPORT_SYMBOL(gspca_disconnect);
 
+#ifdef CONFIG_PM
+int gspca_suspend(struct usb_interface *intf, pm_message_t message)
+{
+       struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
+
+       if (!gspca_dev->streaming)
+               return 0;
+       gspca_dev->frozen = 1;          /* avoid urb error messages */
+       if (gspca_dev->sd_desc->stopN)
+               gspca_dev->sd_desc->stopN(gspca_dev);
+       destroy_urbs(gspca_dev);
+       gspca_set_alt0(gspca_dev);
+       if (gspca_dev->sd_desc->stop0)
+               gspca_dev->sd_desc->stop0(gspca_dev);
+       return 0;
+}
+EXPORT_SYMBOL(gspca_suspend);
+
+int gspca_resume(struct usb_interface *intf)
+{
+       struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
+
+       gspca_dev->frozen = 0;
+       gspca_dev->sd_desc->init(gspca_dev);
+       if (gspca_dev->streaming)
+               return gspca_init_transfer(gspca_dev);
+       return 0;
+}
+EXPORT_SYMBOL(gspca_resume);
+#endif
 /* -- cam driver utility functions -- */
 
 /* auto gain and exposure algorithm based on the knee algorithm described here: