/*
* 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, 5, 0)
+#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 7, 0)
#ifdef GSPCA_DEBUG
int gspca_debug = D_ERR | D_PROBE;
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,
};
cam_pkt_op pkt_scan;
if (urb->status != 0) {
+ if (urb->status == -ESHUTDOWN)
+ return; /* disconnection */
#ifdef CONFIG_PM
if (!gspca_dev->frozen)
#endif
PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
- return; /* disconnection ? */
+ return;
}
pkt_scan = gspca_dev->sd_desc->pkt_scan;
for (i = 0; i < urb->number_of_packets; i++) {
switch (urb->status) {
case 0:
break;
+ case -ESHUTDOWN:
+ return; /* disconnection */
case -ECONNRESET:
urb->status = 0;
break;
if (!gspca_dev->frozen)
#endif
PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
- return; /* disconnection ? */
+ return;
}
/* check the availability of the frame buffer */
if (urb == NULL)
break;
- BUG_ON(!gspca_dev->dev);
gspca_dev->urb[i] = NULL;
- if (!gspca_dev->present)
- usb_kill_urb(urb);
+ usb_kill_urb(urb);
if (urb->transfer_buffer != NULL)
usb_buffer_free(gspca_dev->dev,
urb->transfer_buffer_length,
* look for an input transfer endpoint in an alternate setting
*/
static struct usb_host_endpoint *alt_xfer(struct usb_host_interface *alt,
- __u8 xfer)
+ int xfer)
{
struct usb_host_endpoint *ep;
int i, attr;
for (i = 0; i < alt->desc.bNumEndpoints; i++) {
ep = &alt->endpoint[i];
attr = ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
- if (attr == xfer)
+ if (attr == xfer
+ && ep->desc.wMaxPacketSize != 0)
return ep;
}
return NULL;
{
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],
- USB_ENDPOINT_XFER_ISOC);
+ 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],
- 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;
}
/* 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",
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->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,
gspca_dev->urb[0]->pipe);
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) {
- msleep(20); /* wait for kill
- * complete */
- break; /* try the previous alt */
- }
- goto out;
- }
+ if (ret < 0)
+ break;
}
if (ret >= 0)
break;
+ PDEBUG(D_ERR|D_STREAM,
+ "usb_submit_urb alt %d err %d", gspca_dev->alt, ret);
+ gspca_dev->streaming = 0;
+ destroy_urbs(gspca_dev);
+ if (ret != -ENOSPC)
+ goto out;
+
+ /* the bandwidth is not wide enough
+ * negociate or try a lower alternate setting */
+ 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);
{
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 alt 0 err %d", ret);
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");
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;
+ 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;
+ 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;
+ 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)
{
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;
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 void gspca_release(struct video_device *vfd)
{
struct gspca_dev *gspca_dev = container_of(vfd, struct gspca_dev, vdev);
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,
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];
}
+ } else {
+ ctrls = get_ctrl(gspca_dev, id);
}
- if (ix < 0)
+ if (ctrls == NULL)
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);
return 0;
}
{
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;
+ 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;
+ 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_g_audio(struct file *file, void *priv,
struct v4l2_audio *audio)
{
- memset(audio, 0, sizeof *audio);
strcpy(audio->name, "Microphone");
return 0;
}
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;
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
|| !(gspca_dev->frame[0].v4l2_buf.flags & V4L2_BUF_FLAG_QUEUED)) {
ret = -EINVAL;
return -EINVAL;
if (mutex_lock_interruptible(&gspca_dev->usb_lock))
return -ERESTARTSYS;
- ret = gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp);
+ 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;
}
return -EINVAL;
if (mutex_lock_interruptible(&gspca_dev->usb_lock))
return -ERESTARTSYS;
- ret = gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp);
+ 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;
}
{
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) {
if (mutex_lock_interruptible(&gspca_dev->usb_lock))
return -ERESTARTSYS;
- ret = gspca_dev->sd_desc->get_streamparm(gspca_dev, parm);
+ 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;
}
if (mutex_lock_interruptible(&gspca_dev->usb_lock))
return -ERESTARTSYS;
- ret = gspca_dev->sd_desc->set_streamparm(gspca_dev, parm);
+ 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;
}
-static int vidioc_s_std(struct file *filp, void *priv,
- v4l2_std_id *parm)
-{
- return 0;
-}
-
#ifdef CONFIG_VIDEO_V4L1_COMPAT
static int vidiocgmbuf(struct file *file, void *priv,
struct video_mbuf *mbuf)
{
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;
if (gspca_dev->sd_desc->dq_callback) {
mutex_lock(&gspca_dev->usb_lock);
- gspca_dev->sd_desc->dq_callback(gspca_dev);
+ if (gspca_dev->present)
+ gspca_dev->sd_desc->dq_callback(gspca_dev);
mutex_unlock(&gspca_dev->usb_lock);
}
return j;
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)
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) {
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;
ret = POLLIN | POLLRDNORM; /* something to read */
else
ret = 0;
-out:
mutex_unlock(&gspca_dev->queue_lock);
+ if (!gspca_dev->present)
+ return POLLHUP;
return ret;
}
.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,
+#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
/* 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,
mutex_lock(&gspca_dev->usb_lock);
gspca_dev->present = 0;
- mutex_unlock(&gspca_dev->usb_lock);
- destroy_urbs(gspca_dev);
+ 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);
/* release the device */