summaryrefslogtreecommitdiff
path: root/linux/drivers/media/video/gspca/gspca.c
diff options
context:
space:
mode:
Diffstat (limited to 'linux/drivers/media/video/gspca/gspca.c')
-rw-r--r--linux/drivers/media/video/gspca/gspca.c234
1 files changed, 162 insertions, 72 deletions
diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c
index 943c5981f..cb07f2b2e 100644
--- a/linux/drivers/media/video/gspca/gspca.c
+++ b/linux/drivers/media/video/gspca/gspca.c
@@ -42,8 +42,8 @@ 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, 1, 6)
-static const char version[] = "2.1.6";
+#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7)
+static const char version[] = "2.1.7";
static int video_nr = -1;
@@ -561,11 +561,6 @@ static void gspca_stream_off(struct gspca_dev *gspca_dev)
gspca_set_alt0(gspca_dev);
gspca_dev->sd_desc->stop0(gspca_dev);
PDEBUG(D_STREAM, "stream off OK");
- } else {
- destroy_urbs(gspca_dev);
- atomic_inc(&gspca_dev->nevent);
- wake_up_interruptible(&gspca_dev->wq);
- PDEBUG(D_ERR|D_STREAM, "stream off no device ??");
}
}
@@ -683,9 +678,6 @@ static int try_fmt_vid_cap(struct gspca_dev *gspca_dev,
w = fmt->fmt.pix.width;
h = fmt->fmt.pix.height;
- /* (luvcview problem) */
- if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG)
- fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG;
#ifdef CONFIG_VIDEO_ADV_DEBUG
if (gspca_debug & D_CONF)
PDEBUG_MODE("try fmt cap", fmt->fmt.pix.pixelformat, w, h);
@@ -819,7 +811,7 @@ static int dev_close(struct inode *inode, struct file *file)
return -ERESTARTSYS;
gspca_dev->users--;
- /* if the file did capture, free the streaming resources */
+ /* 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)
@@ -984,7 +976,7 @@ static int vidioc_reqbufs(struct file *file, void *priv,
if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
switch (rb->memory) {
- case GSPCA_MEMORY_READ:
+ case GSPCA_MEMORY_READ: /* (internal call) */
case V4L2_MEMORY_MMAP:
case V4L2_MEMORY_USERPTR:
break;
@@ -994,33 +986,46 @@ static int vidioc_reqbufs(struct file *file, void *priv,
if (mutex_lock_interruptible(&gspca_dev->queue_lock))
return -ERESTARTSYS;
- /* only one file may do capture */
- if ((gspca_dev->capt_file != NULL && gspca_dev->capt_file != file)
- || gspca_dev->streaming) {
+ if (gspca_dev->memory != GSPCA_MEMORY_NO
+ && gspca_dev->memory != rb->memory) {
ret = -EBUSY;
goto out;
}
- if (rb->count == 0) { /* unrequest */
- for (i = 0; i < gspca_dev->nframes; i++) {
- if (gspca_dev->frame[i].vma_use_count) {
- ret = -EBUSY;
- goto out;
- }
- }
- frame_free(gspca_dev);
- gspca_dev->capt_file = NULL;
- } else {
- if (gspca_dev->nframes != 0) {
+ /* only one file may do the capture */
+ if (gspca_dev->capt_file != NULL
+ && gspca_dev->capt_file != file) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /* if allocated, the buffers must not be mapped */
+ for (i = 0; i < gspca_dev->nframes; i++) {
+ if (gspca_dev->frame[i].vma_use_count) {
ret = -EBUSY;
goto out;
}
- gspca_dev->memory = rb->memory;
- ret = frame_alloc(gspca_dev, rb->count);
- if (ret == 0) {
- rb->count = gspca_dev->nframes;
- gspca_dev->capt_file = file;
- }
+ }
+
+ /* stop streaming */
+ if (gspca_dev->streaming) {
+ mutex_lock(&gspca_dev->usb_lock);
+ gspca_stream_off(gspca_dev);
+ mutex_unlock(&gspca_dev->usb_lock);
+ }
+
+ /* free the previous allocated buffers, if any */
+ if (gspca_dev->nframes != 0) {
+ frame_free(gspca_dev);
+ gspca_dev->capt_file = NULL;
+ }
+ if (rb->count == 0) /* unrequest */
+ goto out;
+ gspca_dev->memory = rb->memory;
+ ret = frame_alloc(gspca_dev, rb->count);
+ if (ret == 0) {
+ rb->count = gspca_dev->nframes;
+ gspca_dev->capt_file = file;
}
out:
mutex_unlock(&gspca_dev->queue_lock);
@@ -1062,10 +1067,6 @@ static int vidioc_streamon(struct file *file, void *priv,
ret = -EINVAL;
goto out;
}
- if (gspca_dev->capt_file != file) {
- ret = -EINVAL;
- goto out;
- }
if (!gspca_dev->streaming) {
ret = gspca_init_transfer(gspca_dev);
if (ret < 0)
@@ -1089,7 +1090,7 @@ static int vidioc_streamoff(struct file *file, void *priv,
enum v4l2_buf_type buf_type)
{
struct gspca_dev *gspca_dev = priv;
- int ret;
+ int i, ret;
if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
@@ -1097,18 +1098,23 @@ static int vidioc_streamoff(struct file *file, void *priv,
return 0;
if (mutex_lock_interruptible(&gspca_dev->queue_lock))
return -ERESTARTSYS;
+
+ /* stop streaming */
if (mutex_lock_interruptible(&gspca_dev->usb_lock)) {
ret = -ERESTARTSYS;
goto out;
}
- if (gspca_dev->capt_file != file) {
- ret = -EINVAL;
- goto out2;
- }
gspca_stream_off(gspca_dev);
- ret = 0;
-out2:
mutex_unlock(&gspca_dev->usb_lock);
+
+ /* empty the application queues */
+ for (i = 0; i < gspca_dev->nframes; i++)
+ gspca_dev->frame[i].v4l2_buf.flags &= ~BUF_ALL_FLAGS;
+ 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);
return ret;
@@ -1367,14 +1373,17 @@ static int vidioc_dqbuf(struct file *file, void *priv,
return -EINVAL;
if (v4l2_buf->memory != gspca_dev->memory)
return -EINVAL;
- if (!gspca_dev->streaming)
+
+ /* if not streaming, be sure the application will not loop forever */
+ if (!(file->f_flags & O_NONBLOCK)
+ && !gspca_dev->streaming && gspca_dev->users == 1)
return -EINVAL;
- if (gspca_dev->capt_file != file) {
- ret = -EINVAL;
- goto out;
- }
- /* only one read */
+ /* only the capturing file may dequeue */
+ if (gspca_dev->capt_file != file)
+ return -EINVAL;
+
+ /* only one dequeue / read at a time */
if (mutex_lock_interruptible(&gspca_dev->read_lock))
return -ERESTARTSYS;
@@ -1419,24 +1428,23 @@ static int vidioc_qbuf(struct file *file, void *priv,
if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
+ if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+ return -ERESTARTSYS;
+
index = v4l2_buf->index;
if ((unsigned) index >= gspca_dev->nframes) {
PDEBUG(D_FRAM,
"qbuf idx %d >= %d", index, gspca_dev->nframes);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
- frame = &gspca_dev->frame[index];
-
- if (v4l2_buf->memory != frame->v4l2_buf.memory) {
+ if (v4l2_buf->memory != gspca_dev->memory) {
PDEBUG(D_FRAM, "qbuf bad memory type");
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
- if (gspca_dev->capt_file != file)
- return -EINVAL;
-
- if (mutex_lock_interruptible(&gspca_dev->queue_lock))
- return -ERESTARTSYS;
+ frame = &gspca_dev->frame[index];
if (frame->v4l2_buf.flags & BUF_ALL_FLAGS) {
PDEBUG(D_FRAM, "qbuf bad state");
ret = -EINVAL;
@@ -1495,9 +1503,6 @@ static int read_alloc(struct gspca_dev *gspca_dev,
v4l2_buf.memory = GSPCA_MEMORY_READ;
for (i = 0; i < gspca_dev->nbufread; i++) {
v4l2_buf.index = i;
-/*fixme: ugly!*/
- gspca_dev->frame[i].v4l2_buf.flags |=
- V4L2_BUF_FLAG_MAPPED;
ret = vidioc_qbuf(file, gspca_dev, &v4l2_buf);
if (ret != 0) {
PDEBUG(D_STREAM, "read qbuf err: %d", ret);
@@ -1525,17 +1530,13 @@ static unsigned int dev_poll(struct file *file, poll_table *wait)
if (!gspca_dev->present)
return POLLERR;
- /* if not streaming, the user would use read() */
- if (!gspca_dev->streaming) {
- if (gspca_dev->memory != GSPCA_MEMORY_NO) {
- ret = POLLERR; /* not the 1st time */
- goto out;
- }
+ /* if reqbufs is not done, the user would use read() */
+ if (gspca_dev->nframes == 0) {
+ if (gspca_dev->memory != GSPCA_MEMORY_NO)
+ return POLLERR; /* not the 1st time */
ret = read_alloc(gspca_dev, file);
- if (ret != 0) {
- ret = POLLERR;
- goto out;
- }
+ if (ret != 0)
+ return POLLERR;
}
if (mutex_lock_interruptible(&gspca_dev->queue_lock) != 0)
@@ -1545,6 +1546,7 @@ static unsigned int dev_poll(struct file *file, poll_table *wait)
goto out;
}
+ /* check the next incoming buffer */
i = gspca_dev->fr_o;
i = gspca_dev->fr_queue[i];
if (gspca_dev->frame[i].v4l2_buf.flags & V4L2_BUF_FLAG_DONE)
@@ -1793,6 +1795,94 @@ void gspca_disconnect(struct usb_interface *intf)
}
EXPORT_SYMBOL(gspca_disconnect);
+/* -- cam driver utility functions -- */
+
+/* auto gain and exposure algorithm based on the knee algorithm described here:
+ http://ytse.tricolour.net/docs/LowLightOptimization.html
+
+ Returns 0 if no changes were made, 1 if the gain and or exposure settings
+ where changed. */
+int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum,
+ int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee)
+{
+ int i, steps, gain, orig_gain, exposure, orig_exposure, autogain;
+ const struct ctrl *gain_ctrl = NULL;
+ const struct ctrl *exposure_ctrl = NULL;
+ const struct ctrl *autogain_ctrl = NULL;
+ int retval = 0;
+
+ for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) {
+ if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_GAIN)
+ gain_ctrl = &gspca_dev->sd_desc->ctrls[i];
+ if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_EXPOSURE)
+ exposure_ctrl = &gspca_dev->sd_desc->ctrls[i];
+ if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_AUTOGAIN)
+ autogain_ctrl = &gspca_dev->sd_desc->ctrls[i];
+ }
+ if (!gain_ctrl || !exposure_ctrl || !autogain_ctrl) {
+ PDEBUG(D_ERR, "Error: gspca_auto_gain_n_exposure called "
+ "on cam without (auto)gain/exposure");
+ return 0;
+ }
+
+ if (gain_ctrl->get(gspca_dev, &gain) ||
+ exposure_ctrl->get(gspca_dev, &exposure) ||
+ autogain_ctrl->get(gspca_dev, &autogain) || !autogain)
+ return 0;
+
+ orig_gain = gain;
+ orig_exposure = exposure;
+
+ /* If we are of a multiple of deadzone, do multiple steps to reach the
+ 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",
+ avg_lum, desired_avg_lum, steps);
+
+ for (i = 0; i < steps; i++) {
+ if (avg_lum > desired_avg_lum) {
+ if (gain > gain_knee)
+ gain--;
+ else if (exposure > exposure_knee)
+ exposure--;
+ else if (gain > gain_ctrl->qctrl.default_value)
+ gain--;
+ else if (exposure > exposure_ctrl->qctrl.minimum)
+ exposure--;
+ else if (gain > gain_ctrl->qctrl.minimum)
+ gain--;
+ else
+ break;
+ } else {
+ if (gain < gain_ctrl->qctrl.default_value)
+ gain++;
+ else if (exposure < exposure_knee)
+ exposure++;
+ else if (gain < gain_knee)
+ gain++;
+ else if (exposure < exposure_ctrl->qctrl.maximum)
+ exposure++;
+ else if (gain < gain_ctrl->qctrl.maximum)
+ gain++;
+ else
+ break;
+ }
+ }
+
+ if (gain != orig_gain) {
+ gain_ctrl->set(gspca_dev, gain);
+ retval = 1;
+ }
+ if (exposure != orig_exposure) {
+ exposure_ctrl->set(gspca_dev, exposure);
+ retval = 1;
+ }
+
+ return retval;
+}
+EXPORT_SYMBOL(gspca_auto_gain_n_exposure);
+
/* -- module insert / remove -- */
static int __init gspca_init(void)
{