All of lore.kernel.org
 help / color / mirror / Atom feed
* [New Driver]: stand alone pac207 v4l2 driver
@ 2008-03-28 21:50 Hans de Goede
  0 siblings, 0 replies; only message in thread
From: Hans de Goede @ 2008-03-28 21:50 UTC (permalink / raw)
  To: spca50x-devs, video4linux-list; +Cc: fedora-kernel-list

[-- Attachment #1: Type: text/plain, Size: 660 bytes --]

Hi All,

As explained in my introduction mail I've been working on a standalone v4l2 
driver for pac207 based usb webcams. I've attached the hopefully pretty clean 
result to this mail.

Note that I've also done a version which is split in a generic simple usb 
webcam core and a cam/controller IC specific driver, the split version is the 
one I would like to move forward with.

I'm currently posting these as .c files for easy reading and compilation /
testing, but I still hope to get a lot of feedback / a thorough review, esp of
the core <-> pac207 split version as I hope to submit that as a patch for
mainline inclusion soon.

Thanks & Regards,

Hans


[-- Attachment #2: Makefile --]
[-- Type: text/plain, Size: 156 bytes --]

obj-m += pac207.o

all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

[-- Attachment #3: pac207.c --]
[-- Type: text/plain, Size: 47515 bytes --]

/***************************************************************************
 * Video4Linux2 driver for Pixart PAC207BCA Camera bridge and sensor       *
 *                                                                         *
 * Copyright (C) 2008 by Hans de Goede <j.w.r.degoede@hhs.nl>              *
 * Buffer management code taken from the Video4Linux2 zc030x driver:       *
 * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it>  *
 * PAC207BCA code derived from the gspca Pixart PAC207BCA library:         *
 * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li                 *
 * Copyright (C) 2005 Bertrik.Sikken                                       *
 * Copyleft (C) 2005 Michel Xhaard mxhaard@magic.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 Free Software Foundation; either version 2 of the License, or       *
 * (at your option) any later version.                                     *
 *                                                                         *
 * This program is distributed in the hope that it will be useful,         *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
 * GNU General Public License for more details.                            *
 *                                                                         *
 * You should have received a copy of the GNU General Public License       *
 * along with this program; if not, write to the Free Software             *
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.               *
 ***************************************************************************/

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/param.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/compiler.h>
#include <linux/ioctl.h>
#include <linux/poll.h>
#include <linux/stat.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
#include <linux/page-flags.h>
#include <linux/byteorder/generic.h>
#include <asm/page.h>
#include <asm/uaccess.h>

#include "pac207.h"

/*****************************************************************************/

static __devinitdata struct usb_device_id pac207_id_table[] = {
	{ USB_DEVICE(0x041E, 0x4028) }, /* Creative VistaPlus */
	{ USB_DEVICE(0x093a, 0x2460) }, /* Qtec Wb100 */
	{ USB_DEVICE(0x093a, 0x2463) }, /* Philips SPC220NC */
	{ USB_DEVICE(0x093a, 0x2468) }, /* Generic PAC 207 */
	{ USB_DEVICE(0x093a, 0x2470) }, /* Genius GF112 */   
	{ USB_DEVICE(0x093a, 0x2471) }, /* Genius GF111 */   
	{ USB_DEVICE(0x093a, 0x2472) }, /* Genius GF111 */
	{ 0 }
};

/*****************************************************************************/

MODULE_DEVICE_TABLE(usb, pac207_id_table);
MODULE_AUTHOR("Hans de Goede <j.w.r.degoede@hhs.nl>");
MODULE_DESCRIPTION("Pixart PAC207BCA Camera Driver");
MODULE_LICENSE("GPL");

static unsigned short debug = PAC207_DEBUG_LEVEL;
module_param(debug, ushort, 0644);
MODULE_PARM_DESC(debug,
		 "\n<n> Debugging information level, from 0 to 3:"
		 "\n0 = none (use carefully)"
		 "\n1 = critical errors"
		 "\n2 = significant informations"
		 "\n3 = more verbose messages"
		 "\nLevel 3 is useful for testing only, when only "
		 "one device is used."
		 "\nDefault value is "__MODULE_STRING(PAC207_DEBUG_LEVEL)"."
		 "\n");

/*****************************************************************************/

static const char pac207_sof_marker[5] = { 0xFF, 0xFF, 0x00, 0xFF, 0x96 };

static const u8 pac207_sensor_init[][8] = {
    {0x10, 0x12, 0x0d, 0x12, 0x0c, 0x01, 0x29, 0xf0}, /* 2  */
/*  {0x10, 0x24, 0x06, 0x12, 0x0c, 0x01, 0x29, 0xf0}, ** 2 increase the times exposure decrease frame rate */
    {0x00, 0x64, 0x64, 0x64, 0x04, 0x10, 0xF0, 0x30}, /* a reg_10 digital gain Red Green Blue Ggain */
    {0x00, 0x00, 0x00, 0x70, 0xA0, 0xF8, 0x00, 0x00}, /* 12 */
    {0x00, 0x00, 0x32, 0x00, 0x96, 0x00, 0xA2, 0x02}, /* 40 */
    {0x32, 0x00, 0x96, 0x00, 0xA2, 0x02, 0xAF, 0x00}, /* 42 reg_66 rate control */
};

static const u8 PacReg72[] = { 0x00, 0x00, 0x36, 0x00 }; /* 48 reg_72 Rate Control end BalSize_4a =0x36 */

static const struct v4l2_pix_format pac207_pix_fmt[2] = { {
	.width = 352,
	.height = 288,
	.pixelformat = V4L2_PIX_FMT_SBGGR8,
	.field = V4L2_FIELD_NONE,
	.bytesperline = 0,
	.sizeimage = 352 * 288,
	.colorspace = V4L2_COLORSPACE_SRGB,
	.priv = 8,
}, {
	.width = 176,
	.height = 144,
	.pixelformat = V4L2_PIX_FMT_SBGGR8,
	.field = V4L2_FIELD_NONE,
	.bytesperline = 0,
	.sizeimage = 176 * 144,
	.colorspace = V4L2_COLORSPACE_SRGB,
	.priv = 8,
} };

static struct pac207_decompress_table_t pac207_decompress_table[256];

/*****************************************************************************/

static u32
pac207_request_buffers(struct pac207_device* cam, u32 count)
{
	const struct v4l2_pix_format* p = &pac207_pix_fmt[cam->mode];
	const size_t imagesize = p->width * p->height;
	void* buff = NULL;
	u32 i;

	if (count > PAC207_MAX_FRAMES)
		count = PAC207_MAX_FRAMES;

	cam->nbuffers = count;
	while (cam->nbuffers) {
		if ((buff = vmalloc_32_user(cam->nbuffers *
						PAGE_ALIGN(imagesize))))
			break;
		cam->nbuffers--;
	}

	for (i = 0; i < cam->nbuffers; i++) {
		cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize);
		cam->frame[i].buf.index = i;
		cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize);
		cam->frame[i].buf.length = imagesize;
		cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		cam->frame[i].buf.sequence = 0;
		cam->frame[i].buf.field = V4L2_FIELD_NONE;
		cam->frame[i].buf.memory = V4L2_MEMORY_MMAP;
		cam->frame[i].buf.flags = 0;
		cam->frame[i].buf.bytesused = 0;
		cam->frame[i].state = F_UNUSED;
	}

	return cam->nbuffers;
}


static void pac207_release_buffers(struct pac207_device* cam)
{
	if (cam->nbuffers) {
		vfree(cam->frame[0].bufmem);
		cam->nbuffers = 0;
	}
}


static void pac207_empty_framequeues(struct pac207_device* cam)
{
	u32 i;

	INIT_LIST_HEAD(&cam->inqueue);
	INIT_LIST_HEAD(&cam->outqueue);

	for (i = 0; i < PAC207_MAX_FRAMES; i++) {
		cam->frame[i].state = F_UNUSED;
		cam->frame[i].buf.bytesused = 0;
	}
}


static void pac207_queue_unusedframes(struct pac207_device* cam)
{
	unsigned long lock_flags;
	u32 i;

	for (i = 0; i < cam->nbuffers; i++)
		if (cam->frame[i].state == F_UNUSED) {
			cam->frame[i].state = F_QUEUED;
			spin_lock_irqsave(&cam->queue_lock, lock_flags);
			list_add_tail(&cam->frame[i].frame, &cam->inqueue);
			spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
		}
}

/*****************************************************************************/

int pac207_write_regs(struct pac207_device* cam, u16 index,
	const u8 *buffer, u16 length)
{
	struct usb_device* udev = cam->usbdev;
	int err = 0;
	u8 *kbuffer;

	kbuffer = (u8 *) kmalloc(length, GFP_KERNEL);
	if (!kbuffer) {
		DBG(1, "Not enough memory");
		return -ENOMEM;
	}
	memcpy(kbuffer, buffer, length);

	err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x01, 
			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
			0x00, index, kbuffer, length, PAC207_CTRL_TIMEOUT);
	if (err < 0)
		DBG(1, "Failed to write registers to index 0x%04X, error %d)",
			index, err);

	kfree(kbuffer);

	return err;
}


int pac207_write_reg(struct pac207_device* cam, u16 index, u16 value)
{
	struct usb_device* udev = cam->usbdev;
	int err;

	err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 
			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
			value, index, NULL, 0, PAC207_CTRL_TIMEOUT);
	if (err && err != -ENODEV)
		DBG(1, "Failed to write a register (index 0x%04X, "
			   "value 0x%02X, error %d)",index, value, err);

	return err;
}


int pac207_read_reg(struct pac207_device* cam, u16 index)
{
	struct usb_device* udev = cam->usbdev;
	u8* buff = cam->control_buffer;
	int res;

	res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00,
			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
			0x00, index, buff, 1, PAC207_CTRL_TIMEOUT);
	if (res < 0)
		DBG(1, "Failed to read a register (index 0x%04X, error %d)",
			index, res);

	return (res >= 0) ? (int)(*buff) : res;
}

/*****************************************************************************/

/* auto gain and exposure algorithm based on the knee algorithm described here:
   http://ytse.tricolour.net/docs/LowLightOptimization.html */
static void pac207_do_auto_gain(struct pac207_device* cam)
{
	int i, steps, desired_avg_lum;
	int orig_gain = cam->gain;
	int orig_exposure = cam->exposure;
	int avg_lum = atomic_read(&cam->avg_lum);

	if (!cam->autogain || avg_lum == -1)
		return;

	if (cam->autogain_ignore_frames > 0) {
		cam->autogain_ignore_frames--;
		return;
	}

	/* correct desired lumination for the configured brightness */
	desired_avg_lum = 100 + cam->brightness / 2;

	/* If we are of a multiple of deadzone, do multiple step to reach the
	   desired lumination fast (with the risc of a slight overshoot */
	steps = abs(desired_avg_lum - avg_lum) / PAC207_AUTOGAIN_DEADZONE;

	for (i = 0; i < steps; i++) {
		if (avg_lum > desired_avg_lum) {
			if (cam->gain > PAC207_GAIN_KNEE) {
				cam->gain--;
			} else if (cam->exposure > PAC207_EXPOSURE_KNEE) {
				cam->exposure--;
			} else if (cam->gain > PAC207_GAIN_DEFAULT) {
				cam->gain--;
			} else if (cam->exposure > PAC207_EXPOSURE_MIN) {
				cam->exposure--;
			} else if (cam->gain > PAC207_GAIN_MIN) {
				cam->gain--;
			} else
				break;
		} else { 
			if (cam->gain < PAC207_GAIN_DEFAULT) {
				cam->gain++;
			} else if (cam->exposure < PAC207_EXPOSURE_KNEE) {
				cam->exposure++;
			} else if (cam->gain < PAC207_GAIN_KNEE) {
				cam->gain++;
			} else if (cam->exposure < PAC207_EXPOSURE_MAX) {
				cam->exposure++;
			} else if (cam->gain < PAC207_GAIN_MAX) {
				cam->gain++;
			} else
				break;
		}
	}

	if (cam->exposure != orig_exposure || cam->gain != orig_gain) {
		if (cam->exposure != orig_exposure)
			pac207_write_reg(cam, 0x0002, cam->exposure);
		else
			pac207_write_reg(cam, 0x000e, cam->gain);
		pac207_write_reg(cam, 0x13, 0x01); /* load registers to sen. */
		pac207_write_reg(cam, 0x1c, 0x01); /* not documented */
		cam->autogain_ignore_frames = PAC207_AUTOGAIN_IGNORE_FRAMES;
	}
}

/*****************************************************************************/

static void *pac207_find_sof(struct pac207_device* cam, void* mem,
	unsigned int len)
{
	char *m = mem;
	unsigned int i;

	/* Search for the SOF marker (fixed part) in the header */
	for (i = 0; i < len; i++) {
		if (m[i] == pac207_sof_marker[cam->sof_read]) {
			cam->sof_read++;
			if (cam->sof_read == sizeof(pac207_sof_marker)) {
				DBG(3, "SOF found, bytes to analyze: %u. Frame"
					"starts at byte #%u", len, i + 1);
				cam->sof_read = 0;
				return m + i + 1;
			}
		} else
			cam->sof_read = 0;
	}

	return NULL;
}

#define CLIP(color) (unsigned char)(((color)>0xFF)?0xff:(((color)<0)?0:(color)))

/* Note len is deliberately signed here, where it is unsigned everywhere else
   as it gets compared with the signed bitpos and can even become negative! */
static int pac207_decompress_row(struct pac207_device* cam,
	struct pac207_frame_t* f, u8 *cdata, int len)
{
	const struct v4l2_pix_format *pix_format = &pac207_pix_fmt[cam->mode];
	struct pac207_decoder_state *decoder_state = &cam->decoder_state;
	u8 *outp = f->bufmem + f->buf.bytesused;
	int val, bitlen, bitpos = -decoder_state->no_remaining_bits;
	u8 code;

	/* first two pixels are stored as raw 8-bit */
	while (decoder_state->line_read < 2) {
		*outp++ = *cdata++;
		decoder_state->line_read++;
		len--;
		if (len == 0)
			goto decompress_exit;
	}

	while (decoder_state->line_read < pix_format->width) {
		if (bitpos < 0) {
			code = decoder_state->remaining_bits << (8 + bitpos) |
				cdata[0] >> -bitpos;
		} else {
			u8 *addr = cdata + bitpos / 8;
			code = addr[0] << (bitpos & 7) |
				addr[1] >> (8 - (bitpos & 7));
		}

		bitlen = decoder_state->get_abs ?
				6 : pac207_decompress_table[code].len;

		/* Stop decompressing if we're out of input data */
		if ((bitpos + bitlen) > (len * 8))
			break;

		if (decoder_state->get_abs) {
			*outp++ = code & 0xFC;
			decoder_state->line_read++;
			decoder_state->get_abs = 0;
		} else {
			if (pac207_decompress_table[code].is_abs)
				decoder_state->get_abs = 1;
			else {
				/* relative to left pixel */
				val = outp[-2] +
					pac207_decompress_table[code].val;
				*outp++ = CLIP(val);
				decoder_state->line_read++;
			}
		}
		bitpos += bitlen;
	}

	if (decoder_state->line_read == pix_format->width) {
		/* completely decompressed line, round pos to nearest word */
		len -= 2 * ((bitpos + 15) / 16);
		if (len < 0) {
			decoder_state->discard_byte = 1;
			len = 0;
		}
	} else {
		decoder_state->remaining_bits = cdata[bitpos/8];
		decoder_state->no_remaining_bits = (8 - bitpos) & 7;
		len = 0;
	}

decompress_exit:
	f->buf.bytesused = outp - (u8 *)f->bufmem;
	
	return len;
}

static void pac207_decode_line_init(struct pac207_device* cam)
{
	struct pac207_decoder_state *decoder_state = &cam->decoder_state;

	decoder_state->line_read = 0;
	decoder_state->line_state = LINE_HEADER1;
	decoder_state->no_remaining_bits = 0;
	decoder_state->get_abs = 0; 
}

static void pac207_decode_frame_init(struct pac207_device* cam)
{
	struct pac207_decoder_state *decoder_state = &cam->decoder_state;

	decoder_state->header_read = 0;
	decoder_state->discard_byte = 0;

	pac207_decode_line_init(cam);
}

static int pac207_decode(struct pac207_device* cam, struct pac207_frame_t* f,
	u8 *cdata, unsigned int len)
{
	struct pac207_decoder_state *decoder_state = &cam->decoder_state;
	const struct v4l2_pix_format *pix_format = &pac207_pix_fmt[cam->mode];
	unsigned int needed;

	/* first 11 bytes after sof marker: frame header */
	if (decoder_state->header_read < 11 ) {
		/* get average lumination from frame header (byte 5) */
		if (decoder_state->header_read < 5 ) {
			needed = 5 - decoder_state->header_read;
			if (len >= needed)
				atomic_set(&cam->avg_lum, cdata[needed-1]);
		}
		/* skip the rest of the header */
		needed = 11 - decoder_state->header_read;
		if (len <= needed) {
			decoder_state->header_read += len;
			return 0;
		}
		cdata += needed;
		len -= needed;
		decoder_state->header_read = 11;
	}

	while (len) {
		if (decoder_state->discard_byte) {
			cdata++;
			len--;
			decoder_state->discard_byte = 0;
			continue;
		}

		switch (decoder_state->line_state) {
		case LINE_HEADER1:
			decoder_state->line_marker = cdata[0] << 8;
			decoder_state->line_state = LINE_HEADER2;
			needed = 1;
			break;
		case LINE_HEADER2:
			decoder_state->line_marker |= cdata[0];
			switch (decoder_state->line_marker) {
			case 0x0FF0:
				decoder_state->line_state = LINE_UNCOMPRESSED;
				break;
			case 0x1EE1:
				decoder_state->line_state = LINE_COMPRESSED;
				break;
			default:
				DBG(3, "Error unknown line-header %04X",
					(int)decoder_state->line_marker);
				f->state = F_ERROR;
				return 0;
			}
			needed = 1;
			break;
		case LINE_UNCOMPRESSED:
			needed = pix_format->width - decoder_state->line_read;
			if (needed > len)
				needed = len;
			memcpy(f->bufmem + f->buf.bytesused, cdata, needed);
			f->buf.bytesused += needed;
			decoder_state->line_read += needed;
			break;
		case LINE_COMPRESSED:
			needed = len -
				pac207_decompress_row(cam, f, cdata, len);
			break;
		}

		cdata += needed;
		len -= needed;

		if (decoder_state->line_read == pix_format->width) {
			if (f->buf.bytesused ==
					pix_format->width *
					pix_format->height) {
				/* eureka we've got a frame */
				f->state = F_DONE;
				return 1;
			}
			pac207_decode_line_init(cam);
		}
	}

	return 0;
}

static void pac207_urb_complete(struct urb *urb)
{
	struct pac207_device* cam = urb->context;
	struct pac207_frame_t** f;
	int i, ret;

	switch (urb->status) {
		case 0:
			break;
		case -ENOENT:		/* usb_kill_urb() called. */
		case -ECONNRESET:	/* usb_unlink_urb() called. */
		case -ESHUTDOWN:	/* The endpoint is being disabled. */
			return;
		default:
			goto resubmit_urb;
	}

	f = &cam->frame_current;

	if (!(*f)) {
		if (list_empty(&cam->inqueue))
			goto resubmit_urb;

		(*f) = list_entry(cam->inqueue.next, struct pac207_frame_t,
					frame);
	} 

	for (i = 0; i < urb->number_of_packets; i++) {
		unsigned int len, status;
		void *pos, *sof;

		len = urb->iso_frame_desc[i].actual_length;
		status = urb->iso_frame_desc[i].status;
		pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer;

		if (status) {
			DBG(3, "Error in isochronous frame");
			(*f)->state = F_ERROR;
			continue;
		}

		while (len) {
			sof = pac207_find_sof(cam, pos, len);
			
			if (((*f)->state == F_QUEUED ||
					(*f)->state == F_ERROR) && sof) {
start_of_frame:
				(*f)->state = F_GRABBING;
				(*f)->buf.bytesused = 0;
				do_gettimeofday(&(*f)->buf.timestamp);
				pac207_decode_frame_init(cam);
				len -= sof - pos;
				pos = sof;
				continue; /* check for another sof in packet */
			}

			if ((*f)->state == F_GRABBING) {
				unsigned int n;

				/* If sof decode until sof */
				if (sof) {
					n = sof - pos;
					if (n > sizeof(pac207_sof_marker))
						n -= sizeof(pac207_sof_marker);
					else
						n = 0;
				} else
					n = len;

				if ((ret = pac207_decode(cam, *f, pos, n))) {
					(*f)->buf.sequence= ++cam->frame_count;
					spin_lock(&cam->queue_lock);
					list_move_tail(&(*f)->frame,
						&cam->outqueue);
					if (!list_empty(&cam->inqueue))
						(*f) = list_entry(
							cam->inqueue.next,
							struct pac207_frame_t,
							frame);
					else
						(*f) = NULL;
					spin_unlock(&cam->queue_lock);
					wake_up_interruptible(&cam->wait_frame);
					DBG(3, "Video frame captured");

					if (!(*f))
						goto resubmit_urb;
				}

				if (sof) {
					if (!ret)
						DBG(3, "Incomplete frame");
					goto start_of_frame;
				}

				len -= n;
				pos += n;
			}
			else
				break;
		}
	}

resubmit_urb:
	urb->dev = cam->usbdev;
	ret = usb_submit_urb(urb, GFP_ATOMIC);
	if (ret < 0)
		DBG(1, "usb_submit_urb() failed, error: %d", ret);
}

static void pac207_kill_and_free_urbs(struct pac207_device* cam)
{
	int i;
	struct urb* urb;
	struct usb_device *udev = cam->usbdev;
	
	for (i = 0; i < PAC207_URBS; i++) {
		if (!(urb = cam->urb[i]))
			continue;

		/* Note: usb_kill_urb() is harmless on a not submitted urb */
		usb_kill_urb(urb);

		if (urb->transfer_buffer) {
			usb_buffer_free(udev, urb->transfer_buffer_length,
				urb->transfer_buffer, urb->transfer_dma);
		}
		usb_free_urb(urb);
		cam->urb[i] = NULL;
	}	
}

static void pac207_stop_transfer(struct pac207_device* cam);

static int pac207_start_transfer(struct pac207_device* cam)
{
	struct usb_device *udev = cam->usbdev;
	struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
	struct usb_host_interface* altsetting;
	unsigned int psz;
	struct urb* urb;
	int i, j, alt, err = -ENOSPC;
	u8 mode;

	if (cam->stream == STREAM_ON)
		return -EBUSY;

	for (alt = intf->num_altsetting - 1; alt && err == -ENOSPC; alt--) {
		altsetting = usb_altnum_to_altsetting(intf, alt);

		/* Find our endpoint to determine the packetsize */
		for (i = 0; i < altsetting->desc.bNumEndpoints; i++) {
			if (altsetting->endpoint[i].desc.bEndpointAddress ==
					0x85)
				break;
		}

		/* Endpoint not found or not isoc in this altsetting ?? */
		if (i == altsetting->desc.bNumEndpoints ||
			(altsetting->endpoint[i].desc.bmAttributes &
				USB_ENDPOINT_XFERTYPE_MASK) !=
				USB_ENDPOINT_XFER_ISOC)
			continue;

		psz = le16_to_cpu(altsetting->endpoint[i].desc.wMaxPacketSize);

		/* See paragraph 5.9 / table 5-11 of the usb 2.0 spec. */
		psz = (psz & 0x07ff) * (1 + ((psz >> 11) & 3));

		err = usb_set_interface(udev, 0, alt);
		if (err) {
			DBG(1, "usb_set_interface() failed");
			return err;
		}

		for (i = 0; i < PAC207_URBS; i++) {
			urb = usb_alloc_urb(PAC207_ISO_PACKETS, GFP_KERNEL);
			if (!urb) {
				DBG(1, "usb_alloc_urb() failed");
				pac207_kill_and_free_urbs(cam);
				return -ENOMEM;
			}

			cam->urb[i] = urb;
			/* We alloc psz + 1 bytes per packet, because the
			   decompression code reads bytes at bit offsets and
			   thus can peek up to 7 bits ahead (which will not be
			   used if outside of the actual data, but they will
			   be read)! */
			urb->transfer_buffer = usb_buffer_alloc(udev,
							 (psz + 1) *
							 PAC207_ISO_PACKETS,
							 GFP_KERNEL,
							 &urb->transfer_dma);
			if (!urb->transfer_buffer) {
				DBG(1, "usb_buffer_alloc() failed");
				pac207_kill_and_free_urbs(cam);
				return -ENOMEM;
			}
			urb->dev = udev;
			urb->context = cam;
			urb->pipe = usb_rcvisocpipe(udev, 0x85);
			urb->transfer_flags = URB_ISO_ASAP |
						URB_NO_TRANSFER_DMA_MAP;
			urb->number_of_packets = PAC207_ISO_PACKETS;
			urb->complete = pac207_urb_complete;
			urb->transfer_buffer_length = (psz + 1) *
							PAC207_ISO_PACKETS;
			urb->interval = 1;
			for (j = 0; j < PAC207_ISO_PACKETS; j++) {
				urb->iso_frame_desc[j].offset = (psz + 1) * j;
				urb->iso_frame_desc[j].length = psz;
			}
		}

		/* gspca start methode code here */
		pac207_write_reg(cam, 0x0f, 0x10); /* Power control (Bit 6-0) */
		pac207_write_regs(cam, 0x0002, pac207_sensor_init[0], 8);
		pac207_write_regs(cam, 0x000a, pac207_sensor_init[1], 8);
		pac207_write_regs(cam, 0x0012, pac207_sensor_init[2], 8);
		pac207_write_regs(cam, 0x0040, pac207_sensor_init[3], 8);
		pac207_write_regs(cam, 0x0042, pac207_sensor_init[4], 8);
		pac207_write_regs(cam, 0x0048, PacReg72, 4);
			
		pac207_write_reg(cam, 0x4a, 0x88); /* Compression Balance size */
		pac207_write_reg(cam, 0x4b, 0x00); /* Sram test value */
		pac207_write_reg(cam, 0x08, cam->brightness);
		pac207_write_reg(cam, 0x0e, cam->gain); /* PGA global gain (Bit 4-0) */
		pac207_write_reg(cam, 0x02, cam->exposure); /* PXCK = 12MHz /n */

		mode = 0x02; /* Image Format (Bit 0), LED (1), Compr. test mode (2) */
		if (cam->mode) { /* 176x144 */
			mode |= 0x01;
			DBG(3, "pac207_start mode 176x144");
		} else /* 352x288 */
			DBG(3, "pac207_start mode 352x288");
		pac207_write_reg(cam, 0x41, mode);

		pac207_write_reg(cam, 0x13, 0x01); /* load registers to sensor (Bit 0, auto clear) */
		pac207_write_reg(cam, 0x1c, 0x01); /* not documented */
		udelay(1000); /* taken from gspca */
		pac207_write_reg(cam, 0x40, 0x01); /* Start ISO pipe */

		cam->autogain_ignore_frames = 0;
		atomic_set (&cam->avg_lum, -1);		
		/* end camera specific code */

		cam->stream = STREAM_ON;
		DBG(3, "Stream on");

		for (i = 0; i < PAC207_URBS; i++) {
			err = usb_submit_urb(cam->urb[i], GFP_KERNEL);
			if (err) {
				DBG(1, "usb_submit_urb() failed, error %d",
					err);
				pac207_stop_transfer(cam);
				break;
			}
		}
	}

	return err;
}


static void pac207_stop_transfer(struct pac207_device* cam)
{
	struct usb_device *udev = cam->usbdev;
	s8 i;
	int err = 0;

	if (cam->stream == STREAM_OFF)
		return;

	/* gspca stopN methode code here */
	pac207_write_reg(cam, 0x40, 0x00); /* Stop ISO pipe */
	pac207_write_reg(cam, 0x41, 0x00); /* Turn of LED */
	pac207_write_reg(cam, 0x0f, 0x00); /* Power Control */
	/* end camera specific code */

	pac207_kill_and_free_urbs(cam);

	err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */
	if (err && err != -ENODEV)
		DBG(1, "stop_transfer: usb_set_interface() failed, error: %d",
			err);

	/* gspca stop0 methode code here */

	cam->frame_current = NULL;
	cam->stream = STREAM_OFF;
	DBG(3, "Stream off");
}

/*****************************************************************************/

static int pac207_init(struct pac207_device* cam)
{
	/* gspca configure method code here*/
	pac207_write_reg(cam, 0x41, 0x00); /* 00 Bit_0=Image Format, Bit_1=LED, Bit_2=Compression test mode enable */
	pac207_write_reg(cam, 0x0f, 0x00); /* Power Control */
	pac207_write_reg(cam, 0x11, 0x30); /* Analog Bias */

	return 0;
}

/*****************************************************************************/

static void pac207_release_resources(struct kref *kref)
{
	struct pac207_device *cam = container_of(kref, struct pac207_device,
						 kref);
	DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor);
	video_set_drvdata(cam->v4ldev, NULL);
	video_unregister_device(cam->v4ldev);
	usb_put_dev(cam->usbdev);
	kfree(cam->control_buffer);
	kfree(cam);
}


static int pac207_open(struct inode* inode, struct file* filp)
{
	struct pac207_device* cam;
	int err = 0;

	/* usb driver disconnect may be running */
	if (!mutex_trylock(&pac207_dev_lock))
		return -EAGAIN;

	cam = video_get_drvdata(video_devdata(filp));

	kref_get(&cam->kref);

	if (cam->users) {
		err = -EBUSY;
			goto out;
	}

	/* gspca init methode code here */

	filp->private_data = cam;
	cam->users++;
	cam->io = IO_NONE;
	cam->nreadbuffers = PAC207_DEFAULT_READBUFFERS;
	cam->frame_count = 0;
	cam->brightness = PAC207_BRIGHTNESS_DEFAULT;
	cam->exposure = PAC207_EXPOSURE_DEFAULT;
	cam->gain = PAC207_GAIN_DEFAULT;
	cam->autogain = 1;
	/* init the frame queues */
	pac207_empty_framequeues(cam);

	DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor);

out:
	if (err)
		kref_put(&cam->kref, pac207_release_resources);
	mutex_unlock(&pac207_dev_lock);
	return err;
}


static int pac207_release(struct inode* inode, struct file* filp)
{
	struct pac207_device* cam;

	mutex_lock(&pac207_dev_lock);

	cam = video_get_drvdata(video_devdata(filp));

	pac207_stop_transfer(cam);
	pac207_release_buffers(cam);
	cam->users--;

	DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor);

	kref_put(&cam->kref, pac207_release_resources);

 	mutex_unlock(&pac207_dev_lock);

	return 0;
}


static ssize_t
pac207_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
{
	struct pac207_device* cam = video_get_drvdata(video_devdata(filp));
	struct pac207_frame_t* f, * i;
	unsigned long lock_flags;
	long timeout;
	int err = 0;

	if (mutex_lock_interruptible(&cam->fileop_mutex))
		return -ERESTARTSYS;

	if (cam->io == IO_MMAP) {
		DBG(2, "Close and open the device again to choose the read "
			"method");
		err = -EBUSY;
		goto out;
	}

	if (cam->io == IO_NONE) {
		if (!pac207_request_buffers(cam, cam->nreadbuffers)) {
			DBG(1, "read() failed, not enough memory");
			err = -ENOMEM;
			goto out;
		}

		pac207_queue_unusedframes(cam);

		if ((err = pac207_start_transfer(cam)))
			goto out;

		cam->io = IO_READ;
	}

	/* If the inqueue is depleted, chances are there are some rather old
	   frames in the outqueue (for example an application doing one read
	   every minute), so flush it. */
	if (list_empty(&cam->inqueue)) {
		spin_lock_irqsave(&cam->queue_lock, lock_flags);
		list_for_each_entry(f, &cam->outqueue, frame)
			f->state = F_UNUSED;
		INIT_LIST_HEAD(&cam->outqueue);
		spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
		pac207_queue_unusedframes(cam);
	}

	if (!count)
		goto out;

	if (list_empty(&cam->outqueue)) {
		if (filp->f_flags & O_NONBLOCK) {
			err = -EAGAIN;
			goto out;
		}
		timeout = wait_event_interruptible_timeout(
				cam->wait_frame, !list_empty(&cam->outqueue),
				msecs_to_jiffies(PAC207_FRAME_TIMEOUT) );
		if (timeout <= 0) {
			err = (timeout < 0)? timeout : -EIO;
			goto out;
		}
	}

	pac207_do_auto_gain(cam);

	spin_lock_irqsave(&cam->queue_lock, lock_flags);
	f = list_entry(cam->outqueue.next, struct pac207_frame_t, frame);
	list_del(cam->outqueue.next);
	spin_unlock_irqrestore(&cam->queue_lock, lock_flags);

	if (count > f->buf.bytesused)
		count = f->buf.bytesused;

	if (copy_to_user(buf, f->bufmem, count))
		err = -EFAULT;
	else
		*f_pos += count;

	f->state = F_UNUSED;
	pac207_queue_unusedframes(cam);

out:
	mutex_unlock(&cam->fileop_mutex);

	return err ? err : count;
}


static unsigned int pac207_poll(struct file *filp, poll_table *wait)
{
	struct pac207_device* cam = video_get_drvdata(video_devdata(filp));
	struct pac207_frame_t* f;
	unsigned long lock_flags;
	unsigned int mask = 0;

	if (mutex_lock_interruptible(&cam->fileop_mutex))
		return POLLERR;

	if (cam->io == IO_NONE) {
		if (!pac207_request_buffers(cam, cam->nreadbuffers)) {
			DBG(1, "poll() failed, not enough memory");
			mask = POLLERR;
			goto out;
		}

		pac207_queue_unusedframes(cam);

		if (pac207_start_transfer(cam)) {
			DBG(1, "poll() failed, start transfer failed");
			mask = POLLERR;
			goto out;
		}

		cam->io = IO_READ;
	}

	if (cam->io == IO_READ) {
		/* If the inqueue is depleted, read will flush the outqueue,
		   so do that here, otherwise read will still block. */
		if (list_empty(&cam->inqueue)) {
			spin_lock_irqsave(&cam->queue_lock, lock_flags);
			list_for_each_entry(f, &cam->outqueue, frame)
				f->state = F_UNUSED;
			INIT_LIST_HEAD(&cam->outqueue);
			spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
			pac207_queue_unusedframes(cam);
		}
	}

	poll_wait(filp, &cam->wait_frame, wait);

	if (!list_empty(&cam->outqueue))
		mask |= POLLIN | POLLRDNORM;

out:
	mutex_unlock(&cam->fileop_mutex);
	return mask;
}


static void pac207_vm_open(struct vm_area_struct* vma)
{
	struct pac207_frame_t* f = vma->vm_private_data;
	f->vma_use_count++;
}


static void pac207_vm_close(struct vm_area_struct* vma)
{
	/* NOTE: buffers are not freed here */
	struct pac207_frame_t* f = vma->vm_private_data;
	f->vma_use_count--;
}


static struct vm_operations_struct pac207_vm_ops = {
	.open = pac207_vm_open,
	.close = pac207_vm_close,
};


static int pac207_mmap(struct file* filp, struct vm_area_struct *vma)
{
	struct pac207_device* cam = video_get_drvdata(video_devdata(filp));
	unsigned long size = vma->vm_end - vma->vm_start,
			  start = vma->vm_start;
	void *pos;
	u32 i;
	int err = 0;

	if (mutex_lock_interruptible(&cam->fileop_mutex))
		return -ERESTARTSYS;

	if (!(vma->vm_flags & (VM_WRITE | VM_READ))) {
		err = -EACCES;
		goto out;
	}

	if (cam->io != IO_MMAP ||
			size != PAGE_ALIGN(cam->frame[0].buf.length)) {
		err = -EINVAL;
		goto out;
	}

	for (i = 0; i < cam->nbuffers; i++) {
		if ((cam->frame[i].buf.m.offset>>PAGE_SHIFT) == vma->vm_pgoff)
			break;
	}
	if (i == cam->nbuffers) {
		err = -EINVAL;
		goto out;
	}

	vma->vm_flags |= VM_IO;
	vma->vm_flags |= VM_RESERVED;

	pos = cam->frame[i].bufmem;
	while (size > 0) { /* size is page-aligned */
		if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
			err = -EAGAIN;
			goto out;
		}
		start += PAGE_SIZE;
		pos += PAGE_SIZE;
		size -= PAGE_SIZE;
	}

	vma->vm_ops = &pac207_vm_ops;
	vma->vm_private_data = &cam->frame[i];
	pac207_vm_open(vma);

out:
	mutex_unlock(&cam->fileop_mutex);

	return err;
}

static const struct file_operations pac207_fops = {
	.owner = THIS_MODULE,
	.open =	pac207_open,
	.release = pac207_release,
	.ioctl = video_ioctl2,
	.compat_ioctl = v4l_compat_ioctl32,
	.read =	pac207_read,
	.poll =	pac207_poll,
	.mmap =	pac207_mmap,
	.llseek =  no_llseek,
};

/*****************************************************************************/

static int
pac207_vidioc_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
{
	struct pac207_device* cam = fh;

	cap->version = LINUX_VERSION_CODE;
	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
				V4L2_CAP_STREAMING;
	strcpy(cap->driver, PAC207_NAME);
	strlcpy(cap->card, cam->v4ldev->name, sizeof(cap->card));
	if (usb_make_path(cam->usbdev, cap->bus_info, sizeof(cap->bus_info))
			< 0)
		strlcpy(cap->bus_info, cam->usbdev->dev.bus_id,
			sizeof(cap->bus_info));

	return 0;
}


static int
pac207_vidioc_enuminput(struct file *file, void *fh, struct v4l2_input *inp)
{
	if (inp->index)
		return -EINVAL;

	memset(inp, 0, sizeof(*inp));
	strcpy(inp->name, "Camera");
	inp->type = V4L2_INPUT_TYPE_CAMERA;

	return 0;
}


static int
pac207_vidioc_g_input(struct file *file, void *fh, unsigned int *i)
{
	*i = 0;

	return 0;
}


static int
pac207_vidioc_s_input(struct file *file, void *fh, unsigned int i)
{
	if (i != 0)
		return -EINVAL;

	return 0;
}


static int
pac207_vidioc_query_ctrl(struct file *file, void *fh, struct v4l2_queryctrl *a)
{
	const struct v4l2_queryctrl qctl[] = { {
		.id = V4L2_CID_BRIGHTNESS,
		.type = V4L2_CTRL_TYPE_INTEGER,
		.name = "brightness",
		.minimum = PAC207_BRIGHTNESS_MIN,
		.maximum = PAC207_BRIGHTNESS_MAX,
		.step = 1,
		.default_value = PAC207_BRIGHTNESS_DEFAULT,
		.flags = 0,
	}, {
		.id = V4L2_CID_EXPOSURE,
		.type = V4L2_CTRL_TYPE_INTEGER,
		.name = "exposure",
		.minimum = PAC207_EXPOSURE_MIN,
		.maximum = PAC207_EXPOSURE_MAX,
		.step = 1,
		.default_value = PAC207_EXPOSURE_DEFAULT,
		.flags = 0,
	}, {
		.id = V4L2_CID_AUTOGAIN,
		.type = V4L2_CTRL_TYPE_BOOLEAN,
		.name = "autogain",
		.minimum = 0,
		.maximum = 1,
		.step = 1,
		.default_value = 1,
		.flags = 0,
	}, {
		.id = V4L2_CID_GAIN,
		.type = V4L2_CTRL_TYPE_INTEGER,
		.name = "gain",
		.minimum = PAC207_GAIN_MIN,
		.maximum = PAC207_GAIN_MAX,
		.step = 1,
		.default_value = PAC207_GAIN_DEFAULT,
		.flags = 0,
	} };
	int i;
	
	for (i = 0; i < ARRAY_SIZE(qctl); i++)
		if (qctl[i].id == a->id) {
			memcpy(a, &qctl[i], sizeof(qctl[0]));
			return 0;
		}

	return -EINVAL;
}


static int
pac207_vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *a)
{
	struct pac207_device* cam = fh;

	switch (a->id) {
		case V4L2_CID_BRIGHTNESS:
			a->value = cam->brightness;
			break;
		case V4L2_CID_EXPOSURE:
			a->value = cam->exposure;
			break;
		case V4L2_CID_AUTOGAIN:
			a->value = cam->autogain;
			break;
		case V4L2_CID_GAIN:
			a->value = cam->gain;
			break;
		default:
			return -EINVAL;
	}

	return 0;
}


static int
pac207_vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a)
{
	struct pac207_device* cam = fh;
	int new_value = a->value;
	int err = 0;
	
	if (mutex_lock_interruptible(&cam->fileop_mutex))
		return -ERESTARTSYS;

	if ((err = pac207_vidioc_g_ctrl(file, fh, a)))
		goto out;
	
	if (a->value == new_value)
		goto out;

	/* don't allow mucking with gain / exposure when using autogain */
	if (cam->autogain && (a->id == V4L2_CID_GAIN ||
			a->id == V4L2_CID_EXPOSURE)) {
		err = -EINVAL;
		goto out;
	}

	switch (a->id) {
		case V4L2_CID_BRIGHTNESS:
			cam->brightness = new_value;
			pac207_write_reg(cam, 0x0008, cam->brightness);
			/* give brightness change time to take effect before
			   doing autogain based on the new brightness */
			cam->autogain_ignore_frames =
						PAC207_AUTOGAIN_IGNORE_FRAMES;
			break;

		case V4L2_CID_EXPOSURE:
			cam->exposure = new_value;
			pac207_write_reg(cam, 0x0002, cam->exposure);
			break;

		case V4L2_CID_AUTOGAIN:
			cam->autogain = new_value;
			/* when switching to autogain set defaults to make sure
			   we are on a valid point of the autogain gain /
			   exposure knee graph, and give this change time to
			   take effect before doing autogain. */
			if (cam->autogain) {
				cam->exposure = PAC207_EXPOSURE_DEFAULT;
				cam->gain = PAC207_GAIN_DEFAULT;
				cam->autogain_ignore_frames =
						PAC207_AUTOGAIN_IGNORE_FRAMES;
				pac207_write_reg(cam, 0x0002, cam->exposure);
				pac207_write_reg(cam, 0x000e, cam->gain);
			}
			break;

		case V4L2_CID_GAIN:
			cam->gain = new_value;
			pac207_write_reg(cam, 0x000e, cam->gain);
			break;
	
		/* no default needed already checked in pac207_vidioc_g_ctrl */
	}

	pac207_write_reg(cam, 0x13, 0x01); /* load registers to sensor */
	pac207_write_reg(cam, 0x1c, 0x01); /* not documented */

out:
	mutex_unlock(&cam->fileop_mutex);

	return err;
}


static int
pac207_vidioc_enum_fmt_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
{
	if (f->index != 0)
		return -EINVAL;

	f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	f->flags = 0;
	strcpy(f->description, "bayer rgb");
	f->pixelformat = V4L2_PIX_FMT_SBGGR8;
	memset(&f->reserved, 0, sizeof(f->reserved));

	return 0;
}


static int
pac207_vidioc_g_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
{
	struct pac207_device* cam = fh;
	
	if (mutex_lock_interruptible(&cam->fileop_mutex))
		return -ERESTARTSYS;

	memcpy(&(f->fmt.pix), &pac207_pix_fmt[cam->mode],
		sizeof(pac207_pix_fmt[0]));

	mutex_unlock(&cam->fileop_mutex);

	return 0;
}


static int
pac207_vidioc_try_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
{
	if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_SBGGR8)
		return -EINVAL;

	if (f->fmt.pix.width >= 352 && f->fmt.pix.height >= 288)
		memcpy(&(f->fmt.pix), &pac207_pix_fmt[0],
			sizeof(pac207_pix_fmt[0]));
	else
		memcpy(&(f->fmt.pix), &pac207_pix_fmt[1],
			sizeof(pac207_pix_fmt[0]));

	return 0;
}


static int
pac207_vidioc_s_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
{
	struct pac207_device* cam = fh;
	int err = 0;

	if ((err = pac207_vidioc_try_fmt_cap(file, fh, f)))
		return err;

	if (mutex_lock_interruptible(&cam->fileop_mutex))
		return -ERESTARTSYS;

	if (cam->stream != STREAM_OFF) {
		err = -EBUSY;
		goto out;
	}

	if (f->fmt.pix.width == pac207_pix_fmt[0].width)
		cam->mode = 0;
	else
		cam->mode = 1;

out:
	mutex_unlock(&cam->fileop_mutex);

	return err;
}


static int
pac207_vidioc_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *b)
{
	u32 i;
	struct pac207_device* cam = fh;
	int err = 0;

	if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
			b->memory != V4L2_MEMORY_MMAP)
		return -EINVAL;

	if (mutex_lock_interruptible(&cam->fileop_mutex))
		return -ERESTARTSYS;

	if (cam->io == IO_READ || cam->stream == STREAM_ON) {
		if (cam->io == IO_READ)
			DBG(2, "Close and open the device again to choose the "
				"mmap I/O method");
		err = -EBUSY;
		goto out;
	}

	for (i = 0; i < cam->nbuffers; i++)
		if (cam->frame[i].vma_use_count) {
			DBG(2, "VIDIOC_REQBUFS failed. "
				   "Previous buffers are still mapped.");
			err = -EBUSY;
			goto out;
		}

	pac207_release_buffers(cam);
	if (b->count)
		b->count = pac207_request_buffers(cam, b->count);

	cam->io = b->count ? IO_MMAP : IO_NONE;

out:
	mutex_unlock(&cam->fileop_mutex);

	return err;
}


static int
pac207_vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
{
	struct pac207_device* cam = fh;
	int err = 0;
	
	if (mutex_lock_interruptible(&cam->fileop_mutex))
		return -ERESTARTSYS;

	if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
			b->index >= cam->nbuffers || cam->io != IO_MMAP) {
		err = -EINVAL;
		goto out;
	}

	memcpy(b, &cam->frame[b->index].buf, sizeof(*b));

	if (cam->frame[b->index].vma_use_count)
		b->flags |= V4L2_BUF_FLAG_MAPPED;

	if (cam->frame[b->index].state == F_DONE)
		b->flags |= V4L2_BUF_FLAG_DONE;
	else if (cam->frame[b->index].state != F_UNUSED)
		b->flags |= V4L2_BUF_FLAG_QUEUED;

out:
	mutex_unlock(&cam->fileop_mutex);

	return err;
}


static int
pac207_vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
{
	struct pac207_device* cam = fh;
	unsigned long lock_flags;
	int err = 0;
	
	if (mutex_lock_interruptible(&cam->fileop_mutex))
		return -ERESTARTSYS;

	if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
			b->index >= cam->nbuffers ||
			cam->io != IO_MMAP ||
			cam->frame[b->index].state != F_UNUSED) {
		err = -EINVAL;
		goto out;
	}

	cam->frame[b->index].state = F_QUEUED;

	spin_lock_irqsave(&cam->queue_lock, lock_flags);
	list_add_tail(&cam->frame[b->index].frame, &cam->inqueue);
	spin_unlock_irqrestore(&cam->queue_lock, lock_flags);

out:
	mutex_unlock(&cam->fileop_mutex);

	return err;
}


static int
pac207_vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
{
	struct pac207_device* cam = fh;
	struct pac207_frame_t *f;
	unsigned long lock_flags;
	long timeout;
	int err = 0;
	
	if (mutex_lock_interruptible(&cam->fileop_mutex))
		return -ERESTARTSYS;

	if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP ||
			cam->stream == STREAM_OFF) {
		err = -EINVAL;
		goto out;
	}

	if (list_empty(&cam->outqueue)) {
		if (file->f_flags & O_NONBLOCK) {
			err = -EAGAIN;
			goto out;
		}
		timeout = wait_event_interruptible_timeout(cam->wait_frame,
				!list_empty(&cam->outqueue),
				msecs_to_jiffies(PAC207_FRAME_TIMEOUT) );
		if (timeout <= 0) {
			err = (timeout < 0)? timeout : -EIO;
			goto out;
		}
	}

	pac207_do_auto_gain(cam);

	spin_lock_irqsave(&cam->queue_lock, lock_flags);
	f = list_entry(cam->outqueue.next, struct pac207_frame_t, frame);
	list_del(cam->outqueue.next);
	spin_unlock_irqrestore(&cam->queue_lock, lock_flags);

	f->state = F_UNUSED;

	memcpy(b, &f->buf, sizeof(*b));
	if (f->vma_use_count)
		b->flags |= V4L2_BUF_FLAG_MAPPED;

out:
	mutex_unlock(&cam->fileop_mutex);

	return err;
}


static int
pac207_vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
{
	struct pac207_device* cam = fh;
	int err = 0;
	
	if (mutex_lock_interruptible(&cam->fileop_mutex))
		return -ERESTARTSYS;

	if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) {
		err = -EINVAL;
		goto out;
	}

	err = pac207_start_transfer(cam);

out:
	mutex_unlock(&cam->fileop_mutex);

	return err;
}


static int
pac207_vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
{
	struct pac207_device* cam = fh;
	int err = 0;
	
	if (mutex_lock_interruptible(&cam->fileop_mutex))
		return -ERESTARTSYS;

	if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) {
		err = -EINVAL;
		goto out;
	}

	pac207_stop_transfer(cam);
	pac207_empty_framequeues(cam);

out:
	mutex_unlock(&cam->fileop_mutex);

	return err;
}


static int
pac207_vidioc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
{
	struct pac207_device* cam = fh;

	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
		return -EINVAL;

	a->parm.capture.extendedmode = 0;
	a->parm.capture.readbuffers = cam->nreadbuffers;

	return 0;
}


static int
pac207_vidioc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
{
	struct pac207_device* cam = fh;

	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
		return -EINVAL;

	a->parm.capture.extendedmode = 0;

	if (a->parm.capture.readbuffers == 0)
		a->parm.capture.readbuffers = cam->nreadbuffers;

	if (a->parm.capture.readbuffers > PAC207_MAX_FRAMES)
		a->parm.capture.readbuffers = PAC207_MAX_FRAMES;

	cam->nreadbuffers = a->parm.capture.readbuffers;

	return 0;
}

/*****************************************************************************/

static int
pac207_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
{
	struct usb_device *udev = interface_to_usbdev(intf);
	struct pac207_device* cam;
	int err = 0;
	u8 idreg[] = { 0, 0 };

	if (!(cam = kzalloc(sizeof(struct pac207_device), GFP_KERNEL)))
		return -ENOMEM;

	cam->usbdev = udev;

	if (!(cam->control_buffer = kzalloc(4, GFP_KERNEL))) {
		err = -ENOMEM;
		goto fail;
	}

	idreg[0] = pac207_read_reg(cam, 0x0000);
	idreg[1] = pac207_read_reg(cam, 0x0001);
	idreg[0] = ((idreg[0] & 0x0F) << 4) | ((idreg[1] & 0xf0) >> 4);
	idreg[1] = idreg[1] & 0x0f;
	DBG(2, "Pixart Sensor ID 0x%02X Chips ID 0x%02X !!\n", idreg[0],
		idreg[1]);

	if (idreg[0] != 0x27) {
		DBG(1, "Error invalid sensor ID!");
		err = -ENODEV;
		goto fail;
	}
		
	if (pac207_init(cam)) {
		DBG(1, "Error cam initialization failed");
		err = -ENODEV;
		goto fail;
	}

	if (!(cam->v4ldev = video_device_alloc())) {
		DBG(1, "video_device_alloc() failed");
		err = -ENOMEM;
		goto fail;
	}

	DBG(2, "Pixart PAC207BCA Image Processor and Control Chip detected "
		   "(vid/pid 0x%04X:0x%04X)",id->idVendor, id->idProduct);

	strcpy(cam->v4ldev->name, "Pixart PAC207BCA USB Camera");
	cam->v4ldev->owner = THIS_MODULE;
	cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES;
	cam->v4ldev->fops = &pac207_fops;
	cam->v4ldev->release = video_device_release;
/*	cam->v4ldev->debug = V4L2_DEBUG_IOCTL_ARG; */
	cam->v4ldev->vidioc_querycap = pac207_vidioc_querycap;
	cam->v4ldev->vidioc_enum_input = pac207_vidioc_enuminput;
	cam->v4ldev->vidioc_g_input = pac207_vidioc_g_input;
	cam->v4ldev->vidioc_s_input = pac207_vidioc_s_input;
	cam->v4ldev->vidioc_queryctrl = pac207_vidioc_query_ctrl;
	cam->v4ldev->vidioc_g_ctrl = pac207_vidioc_g_ctrl;
	cam->v4ldev->vidioc_s_ctrl = pac207_vidioc_s_ctrl;
	cam->v4ldev->vidioc_enum_fmt_cap = pac207_vidioc_enum_fmt_cap;
	cam->v4ldev->vidioc_g_fmt_cap = pac207_vidioc_g_fmt_cap;
	cam->v4ldev->vidioc_s_fmt_cap = pac207_vidioc_s_fmt_cap;
	cam->v4ldev->vidioc_try_fmt_cap = pac207_vidioc_try_fmt_cap;
	cam->v4ldev->vidioc_reqbufs = pac207_vidioc_reqbufs;
	cam->v4ldev->vidioc_querybuf = pac207_vidioc_querybuf;
	cam->v4ldev->vidioc_qbuf = pac207_vidioc_qbuf;
	cam->v4ldev->vidioc_dqbuf = pac207_vidioc_dqbuf;
	cam->v4ldev->vidioc_streamon = pac207_vidioc_streamon;
	cam->v4ldev->vidioc_streamoff = pac207_vidioc_streamoff;
	cam->v4ldev->vidioc_g_parm = pac207_vidioc_g_parm;
	cam->v4ldev->vidioc_s_parm = pac207_vidioc_s_parm;
	video_set_drvdata(cam->v4ldev, cam);

	mutex_init(&cam->fileop_mutex);
	spin_lock_init(&cam->queue_lock);
	init_waitqueue_head(&cam->wait_frame);
	kref_init(&cam->kref);

	usb_get_dev(cam->usbdev);

	err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, -1);
	if (err) {
		DBG(1, "V4L2 device registration failed");
		usb_put_dev(cam->usbdev);
		goto fail;
	}

	DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor);

	usb_set_intfdata(intf, cam);

	return 0;

fail:
	if (cam) {
		kfree(cam->control_buffer);
		if (cam->v4ldev)
			video_device_release(cam->v4ldev);
		kfree(cam);
	}
	return err;
}


static void pac207_usb_disconnect(struct usb_interface* intf)
{
	struct pac207_device* cam;

	mutex_lock(&pac207_dev_lock);

	cam = usb_get_intfdata(intf);

	DBG(2, "Disconnecting %s...", cam->v4ldev->name);

	/* No need to keep the urbs and their buffers allocated
	   (no-op when not open and streaming) */
	mutex_lock(&cam->fileop_mutex);
	pac207_stop_transfer(cam);
	mutex_unlock(&cam->fileop_mutex);

	kref_put(&cam->kref, pac207_release_resources);

	mutex_unlock(&pac207_dev_lock);
}


static struct usb_driver pac207_usb_driver = {
	.name =	PAC207_NAME,
	.id_table = pac207_id_table,
	.probe = pac207_usb_probe,
	.disconnect = pac207_usb_disconnect,
};

/*****************************************************************************/

static void pac207_init_decompress_table(void)
{
	int i;
	u8 is_abs, len;
	s8 val;

	for (i = 0; i < 256; i++) {
		is_abs = 0;
		val = 0;
		len = 0;
		if ((i & 0xC0) == 0) {
			/* code 00 */
			val = 0;
			len = 2;
		} else if ((i & 0xC0) == 0x40) {
			/* code 01 */
			val = -5;
			len = 2;
		} else if ((i & 0xC0) == 0x80) {
			/* code 10 */
			val = +5;
			len = 2;
		} else if ((i & 0xF0) == 0xC0) {
			/* code 1100 */
			val = -10;
			len = 4;
		} else if ((i & 0xF0) == 0xD0) {
			/* code 1101 */
			val = +10;
			len = 4;
		} else if ((i & 0xF8) == 0xE0) {
			/* code 11100 */
			val = -15;
			len = 5;
		} else if ((i & 0xF8) == 0xE8) {
			/* code 11101 */
			val = +15;
			len = 5;
		} else if ((i & 0xFC) == 0xF0) {
			/* code 111100 */
			val = -20;
			len = 6;
		} else if ((i & 0xFC) == 0xF4) {
			/* code 111101 */
			val = +20;
			len = 6;
		} else if ((i & 0xF8) == 0xF8) {
			/* code 11111xxxxxx */
			is_abs = 1;
			val = 0;
			len = 5;
		}
		pac207_decompress_table[i].is_abs = is_abs;
		pac207_decompress_table[i].val = val;
		pac207_decompress_table[i].len = len;
	}
}

/*****************************************************************************/

static int __init pac207_module_init(void)
{
	int err = 0;

	pac207_init_decompress_table();

	if ((err = usb_register(&pac207_usb_driver)))
		printk(KERN_ERR PAC207_NAME
			": usb_register() failed, error: %d", err);

	return err;
}


static void __exit pac207_module_exit(void)
{
	usb_deregister(&pac207_usb_driver);
}


module_init(pac207_module_init);
module_exit(pac207_module_exit);

[-- Attachment #4: pac207.h --]
[-- Type: text/plain, Size: 5943 bytes --]

/***************************************************************************
 * Video4Linux2 driver for Pixart PAC207BCA Camera bridge and sensor       *
 *                                                                         *
 * Copyright (C) 2008 by Hans de Goede <j.w.r.degoede@hhs.nl>              *
 * Buffer management code taken from the Video4Linux2 zc030x driver:       *
 * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it>  *
 * PAC207BCA code derived from the gspca Pixart PAC207BCA library:         *
 * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li                 *
 * Copyleft (C) 2005 Michel Xhaard mxhaard@magic.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 Free Software Foundation; either version 2 of the License, or       *
 * (at your option) any later version.                                     *
 *                                                                         *
 * This program is distributed in the hope that it will be useful,         *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
 * GNU General Public License for more details.                            *
 *                                                                         *
 * You should have received a copy of the GNU General Public License       *
 * along with this program; if not, write to the Free Software             *
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.               *
 ***************************************************************************/

#ifndef _PAC207_H_
#define _PAC207_H_

#include <linux/version.h>
#include <linux/usb.h>
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <linux/device.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/time.h>
#include <linux/wait.h>
#include <linux/types.h>
#include <linux/param.h>
#include <linux/mutex.h>
#include <linux/rwsem.h>
#include <linux/stddef.h>
#include <linux/string.h>
#include <linux/kref.h>

/*****************************************************************************/

#define PAC207_DEBUG_LEVEL		2
#define PAC207_MAX_FRAMES		32
#define PAC207_URBS			4 /* 2 is problematic on some systems */
#define PAC207_ISO_PACKETS		16 /* was 7, gspca uses 16 */
#define PAC207_URB_TIMEOUT		msecs_to_jiffies(2 * PAC207_ISO_PACKETS)
#define PAC207_CTRL_TIMEOUT		100
#define PAC207_FRAME_TIMEOUT		2000 /* ms */
#define PAC207_NAME			"pac207"

#define PAC207_BRIGHTNESS_MIN		0
#define PAC207_BRIGHTNESS_MAX		255
#define PAC207_BRIGHTNESS_DEFAULT	4 /* power on default: 4 */

#define PAC207_EXPOSURE_MIN		4
#define PAC207_EXPOSURE_MAX		26
#define PAC207_EXPOSURE_DEFAULT		4 /* power on default: 3 ?? */
#define PAC207_EXPOSURE_KNEE		15

#define PAC207_GAIN_MIN			0
#define PAC207_GAIN_MAX			31
#define PAC207_GAIN_DEFAULT         	9 /* power on default: 9 */
#define PAC207_GAIN_KNEE		20

#define PAC207_AUTOGAIN_DEADZONE	10
/* We calculating the autogain at the end of the transfer of a frame, at this
   moment a frame with the old settings is being transmitted, and a frame is
   being captured with the old settings. So if we adjust the autogain we must
   ignore atleast the 2 next frames for the new settings to come into effect
   before doing any other adjustments */
#define PAC207_AUTOGAIN_IGNORE_FRAMES	3

#define PAC207_DEFAULT_READBUFFERS	3
/*****************************************************************************/

enum pac207_frame_state {
	F_UNUSED,
	F_QUEUED,
	F_GRABBING,
	F_DONE,
	F_ERROR,
};

struct pac207_frame_t {
	void* bufmem;
	struct v4l2_buffer buf;
	enum pac207_frame_state state;
	struct list_head frame;
	unsigned long vma_use_count;
};

enum pac207_io_method {
	IO_NONE,
	IO_READ,
	IO_MMAP,
};

enum pac207_stream_state {
	STREAM_OFF,
	STREAM_ON,
};

enum pac207_line_state {
	LINE_HEADER1,
	LINE_HEADER2,
	LINE_UNCOMPRESSED,
	LINE_COMPRESSED,
};

struct pac207_decompress_table_t {
	u8 is_abs;
	u8 len;
	s8 val;
};
                        
struct pac207_decoder_state {
	u16 line_read;
	u16 line_marker;
	u8 line_state;
	u8 header_read;
	u8 remaining_bits;
	s8 no_remaining_bits;
	u8 get_abs;
	u8 discard_byte;
};

static DEFINE_MUTEX(pac207_dev_lock);

struct pac207_device {
	struct video_device* v4ldev;

	struct usb_device* usbdev;
	struct urb* urb[PAC207_URBS];
	u8* control_buffer;

	struct pac207_frame_t *frame_current, frame[PAC207_MAX_FRAMES];
	struct list_head inqueue, outqueue;
	struct kref kref;
	struct mutex fileop_mutex;
	spinlock_t queue_lock;
	wait_queue_head_t wait_frame;

	struct pac207_decoder_state decoder_state;

	u32 frame_count, nbuffers, nreadbuffers;

	u8 users;
	u8 mode;

	u8 io;
	u8 stream;

	u8 brightness;
	u8 exposure;
	u8 autogain;
	u8 gain;

	u8 sof_read;
	u8 autogain_ignore_frames;

	atomic_t avg_lum;
};

/*****************************************************************************/

#undef DBG
#define DBG(level, fmt, args...)                                              \
do {                                                                          \
	if (debug >= (level)) {                                               \
		if ((level) == 1)                                             \
			dev_err(&cam->usbdev->dev, fmt "\n", ## args);        \
		else if ((level) == 2)                                        \
			dev_info(&cam->usbdev->dev, fmt "\n", ## args);       \
		else if ((level) >= 3)                                        \
			dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n",   \
				 __FILE__, __FUNCTION__, __LINE__ , ## args); \
	}                                                                     \
} while (0)

#endif /* _PAC207_H_ */

[-- Attachment #5: Type: text/plain, Size: 164 bytes --]

--
video4linux-list mailing list
Unsubscribe mailto:video4linux-list-request@redhat.com?subject=unsubscribe
https://www.redhat.com/mailman/listinfo/video4linux-list

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2008-03-28 21:51 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-03-28 21:50 [New Driver]: stand alone pac207 v4l2 driver Hans de Goede

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.