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