#include <asm/page.h>
#include <linux/uaccess.h>
#include <linux/jiffies.h>
+#include <media/v4l2-ioctl.h>
#include "gspca.h"
/* global values */
-#define DEF_NURBS 2 /* default number of URBs (mmap) */
-#define USR_NURBS 5 /* default number of URBs (userptr) */
+#define DEF_NURBS 2 /* default number of URBs */
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, 5)
-static const char version[] = "2.1.5";
+#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 2, 0)
static int video_nr = -1;
}
/* resubmit the URB */
-/*fixme: don't do that when userptr and too many URBs sent*/
urb->status = 0;
st = usb_submit_urb(urb, GFP_ATOMIC);
if (st < 0)
/*
* ISOC message interrupt from the USB device
*
- * Analyse each packet and call the subdriver for copy
- * to the frame buffer.
- *
- * There are 2 functions:
- * - the first one (isoc_irq_mmap) is used when the application
- * buffers are mapped. The frame detection and copy is done
- * at interrupt level.
- * - the second one (isoc_irq_user) is used when the application
- * buffers are in user space (userptr). The frame detection
- * and copy is done by the application.
+ * Analyse each packet and call the subdriver for copy to the frame buffer.
*/
-static void isoc_irq_mmap(struct urb *urb
+static void isoc_irq(struct urb *urb
)
{
struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
- PDEBUG(D_PACK, "isoc irq mmap");
+ PDEBUG(D_PACK, "isoc irq");
if (!gspca_dev->streaming)
return;
fill_frame(gspca_dev, urb);
}
-static void isoc_irq_user(struct urb *urb
-)
-{
- struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
- int i;
-
- PDEBUG(D_PACK, "isoc irq user");
- if (!gspca_dev->streaming)
- return;
-
- i = gspca_dev->urb_in % gspca_dev->nurbs;
- if (urb != gspca_dev->urb[i]) {
- PDEBUG(D_ERR|D_PACK, "urb out of sequence");
- return; /* should never occur */
- }
-
- gspca_dev->urb_in++;
- atomic_inc(&gspca_dev->nevent); /* new event */
- wake_up_interruptible(&gspca_dev->wq);
-/*fixme: submit a new URBs until urb_in == urb_out (% nurbs)*/
-}
-
-/*
- * treat the isoc messages
- *
- * This routine is called by the application (case userptr).
- */
-static void isoc_transfer(struct gspca_dev *gspca_dev)
-{
- struct urb *urb;
- int i;
-
- for (;;) {
- i = gspca_dev->urb_out;
- PDEBUG(D_PACK, "isoc transf i:%d o:%d", gspca_dev->urb_in, i);
- if (i == gspca_dev->urb_in) /* isoc message to read */
- break; /* no (more) message */
- atomic_dec(&gspca_dev->nevent);
-/*PDEBUG(D_PACK, "isoc_trf nevent: %d", atomic_read(&gspca_dev->nevent));*/
- gspca_dev->urb_out = i + 1; /* message treated */
- urb = gspca_dev->urb[i % gspca_dev->nurbs];
- fill_frame(gspca_dev, urb);
- }
-}
-
/*
* add data to the current frame
*
- * This function is called by the subdrivers at interrupt level
- * or user level.
+ * This function is called by the subdrivers at interrupt level.
+ *
* To build a frame, these ones must add
* - one FIRST_PACKET
* - 0 or many INTER_PACKETs
&frame->v4l2_buf.timestamp);
frame->v4l2_buf.sequence = ++gspca_dev->sequence;
} else if (gspca_dev->last_packet_type == DISCARD_PACKET) {
+ if (packet_type == LAST_PACKET)
+ gspca_dev->last_packet_type = packet_type;
return frame;
}
if (len > 0) {
if (frame->data_end - frame->data + len
> frame->v4l2_buf.length) {
- PDEBUG(D_ERR|D_PACK, "frame overflow %d > %d",
+ PDEBUG(D_ERR|D_PACK, "frame overflow %zd > %d",
frame->data_end - frame->data + len,
frame->v4l2_buf.length);
packet_type = DISCARD_PACKET;
} else {
- if (frame->v4l2_buf.memory != V4L2_MEMORY_USERPTR) {
- memcpy(frame->data_end, data, len);
- } else {
- if (copy_to_user(frame->data_end,
- data, len) != 0) {
- PDEBUG(D_ERR|D_PACK,
- "copy to user failed");
- packet_type = DISCARD_PACKET;
- }
- }
+ memcpy(frame->data_end, data, len);
frame->data_end += len;
}
}
/* size = PAGE_ALIGN(size); (already done) */
mem = vmalloc_32(size);
if (mem != NULL) {
- memset(mem, 0, size);
adr = (unsigned long) mem;
while ((long) size > 0) {
SetPageReserved(vmalloc_to_page((void *) adr));
return mem;
}
-static void rvfree(void *mem, unsigned long size)
+static void rvfree(void *mem, long size)
{
unsigned long adr;
- if (!mem)
- return;
adr = (unsigned long) mem;
- while ((long) size > 0) {
+ while (size > 0) {
ClearPageReserved(vmalloc_to_page((void *) adr));
adr += PAGE_SIZE;
size -= PAGE_SIZE;
frsz = gspca_dev->cam.cam_mode[i].sizeimage;
PDEBUG(D_STREAM, "frame alloc frsz: %d", frsz);
frsz = PAGE_ALIGN(frsz);
- PDEBUG(D_STREAM, "new fr_sz: %d", frsz);
gspca_dev->frsz = frsz;
if (count > GSPCA_MAX_FRAMES)
count = GSPCA_MAX_FRAMES;
- if (gspca_dev->memory == V4L2_MEMORY_MMAP) {
- gspca_dev->frbuf = rvmalloc(frsz * count);
- if (!gspca_dev->frbuf) {
- err("frame alloc failed");
- return -ENOMEM;
- }
+ gspca_dev->frbuf = rvmalloc(frsz * count);
+ if (!gspca_dev->frbuf) {
+ err("frame alloc failed");
+ return -ENOMEM;
}
gspca_dev->nframes = count;
for (i = 0; i < count; i++) {
frame->v4l2_buf.length = frsz;
frame->v4l2_buf.memory = gspca_dev->memory;
frame->v4l2_buf.sequence = 0;
- if (gspca_dev->memory == V4L2_MEMORY_MMAP) {
- frame->data = frame->data_end =
+ frame->data = frame->data_end =
gspca_dev->frbuf + i * frsz;
- frame->v4l2_buf.m.offset = i * frsz;
- }
+ frame->v4l2_buf.m.offset = i * frsz;
}
gspca_dev->fr_i = gspca_dev->fr_o = gspca_dev->fr_q = 0;
gspca_dev->last_packet_type = DISCARD_PACKET;
* This routine may be called many times when the bandwidth is too small
* (the bandwidth is checked on urb submit).
*/
-struct usb_host_endpoint *get_isoc_ep(struct gspca_dev *gspca_dev)
+static struct usb_host_endpoint *get_isoc_ep(struct gspca_dev *gspca_dev)
{
struct usb_interface *intf;
struct usb_host_endpoint *ep;
{
struct urb *urb;
int n, nurbs, i, psize, npkt, bsize;
- usb_complete_t usb_complete;
/* calculate the packet size and the number of packets */
psize = le16_to_cpu(ep->desc.wMaxPacketSize);
bsize = psize * npkt;
PDEBUG(D_STREAM,
"isoc %d pkts size %d (bsize:%d)", npkt, psize, bsize);
-/*fixme:don't submit all URBs when userptr*/
- if (gspca_dev->memory != V4L2_MEMORY_USERPTR) {
- usb_complete = isoc_irq_mmap;
- nurbs = DEF_NURBS;
- } else {
- usb_complete = isoc_irq_user;
- nurbs = USR_NURBS;
- }
+ nurbs = DEF_NURBS;
gspca_dev->nurbs = nurbs;
for (n = 0; n < nurbs; n++) {
urb = usb_alloc_urb(npkt, GFP_KERNEL);
urb->transfer_flags = URB_ISO_ASAP
| URB_NO_TRANSFER_DMA_MAP;
urb->interval = ep->desc.bInterval;
- urb->complete = usb_complete;
+ urb->complete = isoc_irq;
urb->number_of_packets = npkt;
urb->transfer_buffer_length = bsize;
for (i = 0; i < npkt; i++) {
urb->iso_frame_desc[i].offset = psize * i;
}
}
- gspca_dev->urb_in = gspca_dev->urb_out = 0;
return 0;
}
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 ??");
}
}
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);
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)
if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
switch (rb->memory) {
+ case GSPCA_MEMORY_READ: /* (internal call) */
case V4L2_MEMORY_MMAP:
case V4L2_MEMORY_USERPTR:
break;
if (mutex_lock_interruptible(&gspca_dev->queue_lock))
return -ERESTARTSYS;
+ if (gspca_dev->memory != GSPCA_MEMORY_NO
+ && gspca_dev->memory != rb->memory) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /* 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;
}
}
- /* only one file may do capture */
- if ((gspca_dev->capt_file != NULL && gspca_dev->capt_file != file)
- || gspca_dev->streaming) {
- ret = -EBUSY;
- goto out;
+ /* stop streaming */
+ if (gspca_dev->streaming) {
+ mutex_lock(&gspca_dev->usb_lock);
+ gspca_stream_off(gspca_dev);
+ mutex_unlock(&gspca_dev->usb_lock);
}
- if (rb->count == 0) { /* unrequest? */
+ /* free the previous allocated buffers, if any */
+ if (gspca_dev->nframes != 0) {
frame_free(gspca_dev);
gspca_dev->capt_file = NULL;
- } else {
- 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;
- }
+ }
+ 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);
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)
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;
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;
struct page *page;
unsigned long addr, start, size;
int i, ret;
-#ifdef CONFIG_VIDEO_V4L1_COMPAT
- int compat = 0;
-#endif
start = vma->vm_start;
size = vma->vm_end - vma->vm_start;
goto out;
}
#ifdef CONFIG_VIDEO_V4L1_COMPAT
- if (i == 0 && size == frame->v4l2_buf.length * gspca_dev->nframes)
- compat = 1;
- else
+ /* v4l1 maps all the buffers */
+ if (i != 0
+ || size != frame->v4l2_buf.length * gspca_dev->nframes)
#endif
- if (size != frame->v4l2_buf.length) {
+ if (size != frame->v4l2_buf.length) {
PDEBUG(D_STREAM, "mmap bad size");
ret = -EINVAL;
goto out;
vma->vm_ops = &gspca_vm_ops;
vma->vm_private_data = frame;
gspca_vm_open(vma);
-#ifdef CONFIG_VIDEO_V4L1_COMPAT
- if (compat) {
-/*fixme: ugly*/
- for (i = 1; i < gspca_dev->nframes; ++i)
- gspca_dev->frame[i].v4l2_buf.flags |=
- V4L2_BUF_FLAG_MAPPED;
- }
-#endif
ret = 0;
out:
mutex_unlock(&gspca_dev->queue_lock);
struct gspca_frame *frame;
int i, j, ret;
- /* if userptr, treat the awaiting URBs */
- if (gspca_dev->memory == V4L2_MEMORY_USERPTR)
- isoc_transfer(gspca_dev);
-
/* check if a frame is ready */
i = gspca_dev->fr_o;
j = gspca_dev->fr_queue[i];
atomic_dec(&gspca_dev->nevent);
if (!gspca_dev->streaming || !gspca_dev->present)
return -EIO;
- if (gspca_dev->memory == V4L2_MEMORY_USERPTR)
- isoc_transfer(gspca_dev);
i = gspca_dev->fr_o;
j = gspca_dev->fr_queue[i];
frame = &gspca_dev->frame[j];
gspca_dev->fr_i,
gspca_dev->fr_o);
- if (gspca_dev->sd_desc->dq_callback)
+ if (gspca_dev->sd_desc->dq_callback) {
+ mutex_lock(&gspca_dev->usb_lock);
gspca_dev->sd_desc->dq_callback(gspca_dev);
-
+ mutex_unlock(&gspca_dev->usb_lock);
+ }
return j;
}
int i, ret;
PDEBUG(D_FRAM, "dqbuf");
- if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE
- || (v4l2_buf->memory != V4L2_MEMORY_MMAP
- && v4l2_buf->memory != V4L2_MEMORY_USERPTR))
+ if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- if (!gspca_dev->streaming)
+ if (v4l2_buf->memory != gspca_dev->memory)
return -EINVAL;
- if (gspca_dev->capt_file != file) {
- ret = -EINVAL;
- goto out;
- }
- /* only one read */
+ /* 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;
+
+ /* 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;
goto out;
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,
+ frame->data,
+ frame->v4l2_buf.bytesused)) {
+ PDEBUG(D_ERR|D_STREAM,
+ "dqbuf cp to user failed");
+ ret = -EFAULT;
+ goto out;
+ }
+ }
frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE;
memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf);
PDEBUG(D_FRAM, "dqbuf %d", i);
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;
/* frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE; */
if (frame->v4l2_buf.memory == V4L2_MEMORY_USERPTR) {
- frame->data = frame->data_end =
- (__u8 *) v4l2_buf->m.userptr;
frame->v4l2_buf.m.userptr = v4l2_buf->m.userptr;
frame->v4l2_buf.length = v4l2_buf->length;
}
memset(&rb, 0, sizeof rb);
rb.count = gspca_dev->nbufread;
rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- rb.memory = V4L2_MEMORY_MMAP;
+ rb.memory = GSPCA_MEMORY_READ;
ret = vidioc_reqbufs(file, gspca_dev, &rb);
if (ret != 0) {
PDEBUG(D_STREAM, "read reqbuf err %d", ret);
}
memset(&v4l2_buf, 0, sizeof v4l2_buf);
v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- v4l2_buf.memory = V4L2_MEMORY_MMAP;
+ 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);
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)
goto out;
}
- /* if userptr, treat the awaiting URBs */
- if (gspca_dev->memory == V4L2_MEMORY_USERPTR
- && gspca_dev->capt_file == file)
- isoc_transfer(gspca_dev);
-
+ /* 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)
struct timeval timestamp;
int n, ret, ret2;
- PDEBUG(D_FRAM, "read (%d)", count);
+ PDEBUG(D_FRAM, "read (%zd)", count);
if (!gspca_dev->present)
return -ENODEV;
switch (gspca_dev->memory) {
for (;;) {
memset(&v4l2_buf, 0, sizeof v4l2_buf);
v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- v4l2_buf.memory = V4L2_MEMORY_MMAP;
+ v4l2_buf.memory = GSPCA_MEMORY_READ;
ret = vidioc_dqbuf(file, gspca_dev, &v4l2_buf);
if (ret != 0) {
PDEBUG(D_STREAM, "read dqbuf err %d", ret);
}
/* copy the frame */
- if (count < frame->v4l2_buf.bytesused) {
- PDEBUG(D_STREAM, "read bad count: %d < %d",
- count, frame->v4l2_buf.bytesused);
-/*fixme: special errno?*/
- ret = -EINVAL;
- goto out;
- }
- count = frame->v4l2_buf.bytesused;
+ if (count > frame->v4l2_buf.bytesused)
+ count = frame->v4l2_buf.bytesused;
ret = copy_to_user(data, frame->data, count);
if (ret != 0) {
PDEBUG(D_ERR|D_STREAM,
- "read cp to user lack %d / %d", ret, count);
+ "read cp to user lack %d / %zd", ret, count);
ret = -EFAULT;
goto out;
}
/* init video stuff */
memcpy(&gspca_dev->vdev, &gspca_template, sizeof gspca_template);
- gspca_dev->vdev.dev = &dev->dev;
+ 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 */
}
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)
{
- info("main v%s registered", version);
+ info("main v%d.%d.%d registered",
+ (DRIVER_VERSION_NUMBER >> 16) & 0xff,
+ (DRIVER_VERSION_NUMBER >> 8) & 0xff,
+ DRIVER_VERSION_NUMBER & 0xff);
return 0;
}
static void __exit gspca_exit(void)