[New Driver]: stand alone pac207 v4l2 driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

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
/***************************************************************************
 * Video4Linux2 driver for Pixart PAC207BCA Camera bridge and sensor       *
 *                                                                         *
 * Copyright (C) 2008 by Hans de Goede <j.w.r.degoede@xxxxxx>              *
 * Buffer management code taken from the Video4Linux2 zc030x driver:       *
 * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@xxxxxxxxxxxxxxx>  *
 * PAC207BCA code derived from the gspca Pixart PAC207BCA library:         *
 * Copyright (C) 2005 Thomas Kaiser thomas@xxxxxxxxxxxxxxx                 *
 * Copyright (C) 2005 Bertrik.Sikken                                       *
 * Copyleft (C) 2005 Michel Xhaard mxhaard@xxxxxxxx                        *
 *                                                                         *
 * 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@xxxxxx>");
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);
/***************************************************************************
 * Video4Linux2 driver for Pixart PAC207BCA Camera bridge and sensor       *
 *                                                                         *
 * Copyright (C) 2008 by Hans de Goede <j.w.r.degoede@xxxxxx>              *
 * Buffer management code taken from the Video4Linux2 zc030x driver:       *
 * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@xxxxxxxxxxxxxxx>  *
 * PAC207BCA code derived from the gspca Pixart PAC207BCA library:         *
 * Copyright (C) 2005 Thomas Kaiser thomas@xxxxxxxxxxxxxxx                 *
 * Copyleft (C) 2005 Michel Xhaard mxhaard@xxxxxxxx                        *
 *                                                                         *
 * 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_ */
_______________________________________________
Fedora-kernel-list mailing list
Fedora-kernel-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/fedora-kernel-list

[Index of Archives]     [Fedora General Discussion]     [Older Fedora Users Archive]     [Fedora Advisory Board]     [Fedora Security]     [Fedora Devel Java]     [Fedora Legacy]     [Fedora Desktop]     [ATA RAID]     [Fedora Marketing]     [Fedora Mentors]     [Fedora Package Announce]     [Fedora Package Review]     [Fedora Music]     [Fedora Packaging]     [Centos]     [Fedora SELinux]     [Coolkey]     [Yum Users]     [Tux]     [Yosemite News]     [KDE Users]     [Fedora Art]     [Fedora Docs]     [USB]     [Asterisk PBX]

  Powered by Linux