#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>
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;
/* 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,
}
/* 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);
{
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
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);
+ }
}
/*
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;
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) {
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 */
{
gspca_dev->streaming = 0;
atomic_set(&gspca_dev->nevent, 0);
- 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);
- if (gspca_dev->sd_desc->stop0)
- gspca_dev->sd_desc->stop0(gspca_dev);
- PDEBUG(D_STREAM, "stream off OK");
- }
+ 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->sd_desc->stop0)
+ gspca_dev->sd_desc->stop0(gspca_dev);
+ PDEBUG(D_STREAM, "stream off OK");
}
static void gspca_set_default_mode(struct gspca_dev *gspca_dev)
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;
}
}
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);
ret = -EBUSY;
goto out;
}
- gspca_dev->users++;
- /* one more user */
- kref_get(&gspca_dev->kref);
+ /* protect the subdriver against rmmod */
+ if (!try_module_get(gspca_dev->module)) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ gspca_dev->users++;
file->private_data = gspca_dev;
#ifdef GSPCA_DEBUG
gspca_dev->memory = GSPCA_MEMORY_NO;
}
file->private_data = NULL;
+ module_put(gspca_dev->module);
mutex_unlock(&gspca_dev->queue_lock);
PDEBUG(D_STREAM, "close done");
- kref_put(&gspca_dev->kref, gspca_delete);
-
return 0;
}
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;
return ret;
}
-static void dev_release(struct video_device *vfd)
-{
- /* nothing */
-}
-
static struct file_operations dev_fops = {
.owner = THIS_MODULE,
.open = dev_open,
.name = "gspca main driver",
.fops = &dev_fops,
.ioctl_ops = &dev_ioctl_ops,
- .release = dev_release, /* mandatory */
+ .release = gspca_release,
.minor = -1,
};
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");
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);
init_waitqueue_head(&gspca_dev->wq);
/* init video stuff */
- memcpy(&gspca_dev->vdev, &gspca_template, sizeof gspca_template);
- gspca_dev->vdev.parent = &dev->dev;
- memcpy(&gspca_dev->fops, &dev_fops, sizeof gspca_dev->fops);
- gspca_dev->vdev.fops = &gspca_dev->fops;
- gspca_dev->fops.owner = module; /* module protection */
+ gspca_dev->vdev = video_device_alloc();
+ memcpy(gspca_dev->vdev, &gspca_template, sizeof gspca_template);
+ gspca_dev->vdev->parent = &dev->dev;
+ gspca_dev->module = module;
gspca_dev->present = 1;
ret = video_register_device(&gspca_dev->vdev,
VFL_TYPE_GRABBER,
PDEBUG(D_PROBE, "probe ok");
return 0;
out:
- kref_put(&gspca_dev->kref, gspca_delete);
+ kfree(gspca_dev->usb_buf);
+ kfree(gspca_dev);
return ret;
}
EXPORT_SYMBOL(gspca_dev_probe);
usb_set_intfdata(intf, NULL);
-/* We don't want people trying to open up the device */
+ /* release the device */
+ /* (this will call gspca_release() immediatly or on last close) */
video_unregister_device(&gspca_dev->vdev);
- gspca_dev->present = 0;
- gspca_dev->streaming = 0;
-
- kref_put(&gspca_dev->kref, gspca_delete);
-
PDEBUG(D_PROBE, "disconnect complete");
}
EXPORT_SYMBOL(gspca_disconnect);
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++) {