* [New Driver]: usbvideo2 webcam core + pac207 driver using it.
@ 2008-03-28 21:52 Hans de Goede
0 siblings, 0 replies; 7+ messages in thread
From: Hans de Goede @ 2008-03-28 21:52 UTC (permalink / raw)
To: spca50x-devs, video4linux-list; +Cc: fedora-kernel-list
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.
This is the promised split version of the pac207 driver I've been working on, I
would like to ask everyone to take a good look at this, as I plan to base a
number of other (gspca derived) v4l2 drivers on this same core.
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
--
video4linux-list mailing list
Unsubscribe mailto:video4linux-list-request@redhat.com?subject=unsubscribe
https://www.redhat.com/mailman/listinfo/video4linux-list
^ permalink raw reply [flat|nested] 7+ messages in thread
* [New Driver]: usbvideo2 webcam core + pac207 driver using it.
@ 2008-03-28 21:53 Hans de Goede
2008-04-03 21:27 ` Brandon Philips
[not found] ` <20080404184855.963369c5.zaitcev@redhat.com>
0 siblings, 2 replies; 7+ messages in thread
From: Hans de Goede @ 2008-03-28 21:53 UTC (permalink / raw)
To: spca50x-devs, video4linux-list; +Cc: fedora-kernel-list
[-- Attachment #1: Type: text/plain, Size: 723 bytes --]
<Sorry, this time with attachments>
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.
This is the promised split version of the pac207 driver I've been working on, I
would like to ask everyone to take a good look at this, as I plan to base a
number of other (gspca derived) v4l2 drivers on this same core.
I'm currently posting these as .c files for easy reading and compilation /
testing, but I still hope to get a lot of feedback / a thorough review, esp of
the core <-> pac207 split version as I hope to submit that as a patch for
mainline inclusion soon.
Thanks & Regards,
Hans
[-- Attachment #2: Makefile --]
[-- Type: text/plain, Size: 168 bytes --]
obj-m += usbvideo2.o pac207.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
[-- Attachment #3: TODO --]
[-- Type: text/plain, Size: 186 bytes --]
-unlock fileop_mutex while waiting for frame in dqbuf, since when unlocked
the v4ldev could be unregistered (disconnect) this means that dqbuf should
be handled outside video2_ioctl()
[-- Attachment #4: pac207.c --]
[-- Type: text/plain, Size: 26101 bytes --]
/***************************************************************************
* Video4Linux2 driver for Pixart PAC207BCA Camera bridge and sensor *
* *
* Copyright (C) 2008 by Hans de Goede <j.w.r.degoede@hhs.nl> *
* PAC207BCA code derived from the gspca Pixart PAC207BCA library: *
* Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li *
* Copyright (C) 2005 Bertrik.Sikken *
* Copyleft (C) 2005 Michel Xhaard mxhaard@magic.fr *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
***************************************************************************/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/param.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/compiler.h>
#include <linux/ioctl.h>
#include <linux/poll.h>
#include <linux/stat.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
#include <linux/page-flags.h>
#include <linux/byteorder/generic.h>
#include <asm/page.h>
#include <asm/uaccess.h>
#include "usbvideo2.h"
/*****************************************************************************/
static __devinitdata struct usb_device_id pac207_id_table[] = {
{ USB_DEVICE(0x041E, 0x4028) }, /* Creative VistaPlus */
{ USB_DEVICE(0x093a, 0x2460) }, /* Qtec Wb100 */
{ USB_DEVICE(0x093a, 0x2463) }, /* Philips SPC220NC */
{ USB_DEVICE(0x093a, 0x2468) }, /* Generic PAC 207 */
{ USB_DEVICE(0x093a, 0x2470) }, /* Genius GF112 */
{ USB_DEVICE(0x093a, 0x2471) }, /* Genius GF111 */
{ USB_DEVICE(0x093a, 0x2472) }, /* Genius GF111 */
{ 0 }
};
/*****************************************************************************/
MODULE_DEVICE_TABLE(usb, pac207_id_table);
MODULE_AUTHOR("Hans de Goede <j.w.r.degoede@hhs.nl>");
MODULE_DESCRIPTION("Pixart PAC207BCA Camera Driver");
MODULE_LICENSE("GPL");
/*****************************************************************************/
#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_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;
};
struct pac207_data {
struct pac207_decoder_state decoder_state;
u8 mode;
u8 brightness;
u8 exposure;
u8 autogain;
u8 gain;
u8 sof_read;
u8 autogain_ignore_frames;
atomic_t avg_lum;
};
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];
/*****************************************************************************/
int pac207_write_regs(struct usbvideo2_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, USBVIDEO2_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 usbvideo2_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, USBVIDEO2_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 usbvideo2_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, USBVIDEO2_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 usbvideo2_device* cam)
{
int i, steps, desired_avg_lum;
struct pac207_data *data = cam->cam_data;
int orig_gain = data->gain;
int orig_exposure = data->exposure;
int avg_lum = atomic_read(&data->avg_lum);
if (!data->autogain || avg_lum == -1)
return;
if (data->autogain_ignore_frames > 0) {
data->autogain_ignore_frames--;
return;
}
/* correct desired lumination for the configured brightness */
desired_avg_lum = 100 + data->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 (data->gain > PAC207_GAIN_KNEE) {
data->gain--;
} else if (data->exposure > PAC207_EXPOSURE_KNEE) {
data->exposure--;
} else if (data->gain > PAC207_GAIN_DEFAULT) {
data->gain--;
} else if (data->exposure > PAC207_EXPOSURE_MIN) {
data->exposure--;
} else if (data->gain > PAC207_GAIN_MIN) {
data->gain--;
} else
break;
} else {
if (data->gain < PAC207_GAIN_DEFAULT) {
data->gain++;
} else if (data->exposure < PAC207_EXPOSURE_KNEE) {
data->exposure++;
} else if (data->gain < PAC207_GAIN_KNEE) {
data->gain++;
} else if (data->exposure < PAC207_EXPOSURE_MAX) {
data->exposure++;
} else if (data->gain < PAC207_GAIN_MAX) {
data->gain++;
} else
break;
}
}
if (data->exposure != orig_exposure || data->gain != orig_gain) {
if (data->exposure != orig_exposure)
pac207_write_reg(cam, 0x0002, data->exposure);
else
pac207_write_reg(cam, 0x000e, data->gain);
pac207_write_reg(cam, 0x13, 0x01); /* load registers to sen. */
pac207_write_reg(cam, 0x1c, 0x01); /* not documented */
data->autogain_ignore_frames = PAC207_AUTOGAIN_IGNORE_FRAMES;
}
}
/*****************************************************************************/
static u8 *pac207_find_sof(struct usbvideo2_device* cam, u8 *m,
unsigned int len, int repeat)
{
struct pac207_data *data = cam->cam_data;
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[data->sof_read]) {
data->sof_read++;
if (data->sof_read == sizeof(pac207_sof_marker)) {
DBG(3, "SOF found, bytes to analyze: %u. Frame"
"starts at byte #%u", len, i + 1);
data->sof_read = 0;
return m + i + 1;
}
} else
data->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 usbvideo2_device* cam,
struct usbvideo2_frame_t* f, u8 *cdata, int len)
{
struct pac207_data *data = cam->cam_data;
const struct v4l2_pix_format *pix_format = &pac207_pix_fmt[data->mode];
struct pac207_decoder_state *decoder_state = &data->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 usbvideo2_device* cam)
{
struct pac207_data *data = cam->cam_data;
struct pac207_decoder_state *decoder_state = &data->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 usbvideo2_device* cam)
{
struct pac207_data *data = cam->cam_data;
struct pac207_decoder_state *decoder_state = &data->decoder_state;
decoder_state->header_read = 0;
decoder_state->discard_byte = 0;
pac207_decode_line_init(cam);
}
static int pac207_decode(struct usbvideo2_device *cam,
struct usbvideo2_frame_t *f, u8 *data, unsigned int len)
{
struct pac207_data *cam_data = cam->cam_data;
struct pac207_decoder_state *decoder_state = &cam_data->decoder_state;
const struct v4l2_pix_format *pix_format =
&pac207_pix_fmt[cam_data->mode];
unsigned int needed = 0;
/* 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_data->avg_lum, data[needed-1]);
}
/* skip the rest of the header */
needed = 11 - decoder_state->header_read;
if (len <= needed) {
decoder_state->header_read += len;
return 0;
}
data += needed;
len -= needed;
decoder_state->header_read = 11;
}
while (len) {
if (decoder_state->discard_byte) {
data++;
len--;
decoder_state->discard_byte = 0;
continue;
}
switch (decoder_state->line_state) {
case LINE_HEADER1:
decoder_state->line_marker = data[0] << 8;
decoder_state->line_state = LINE_HEADER2;
needed = 1;
break;
case LINE_HEADER2:
decoder_state->line_marker |= data[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, data, needed);
f->buf.bytesused += needed;
decoder_state->line_read += needed;
break;
case LINE_COMPRESSED:
needed = len -
pac207_decompress_row(cam, f, data, len);
break;
}
data += needed;
len -= needed;
if (decoder_state->line_read == pix_format->width) {
if (f->buf.bytesused == cam->imagesize) {
/* eureka we've got a frame */
f->state = F_DONE;
return 1;
}
pac207_decode_line_init(cam);
}
}
return 0;
}
/*****************************************************************************/
static int pac207_start_transfer(struct usbvideo2_device* cam)
{
u8 mode;
struct pac207_data *data = cam->cam_data;
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, data->brightness);
pac207_write_reg(cam, 0x0e, data->gain); /* PGA global gain (Bit 4-0) */
pac207_write_reg(cam, 0x02, data->exposure); /* PXCK = 12MHz /n */
mode = 0x02; /* Image Format (Bit 0), LED (1), Compr. test mode (2) */
if (data->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 */
data->sof_read = 0;
data->autogain_ignore_frames = 0;
atomic_set (&data->avg_lum, -1);
return 0;
}
static void pac207_stop_transfer_pre_kill_urbs(struct usbvideo2_device* cam)
{
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 */
}
/*****************************************************************************/
static int pac207_open(struct usbvideo2_device* cam)
{
struct pac207_data *data = cam->cam_data;
data->mode = 0;
cam->imagesize = 352 * 288;
data->brightness = PAC207_BRIGHTNESS_DEFAULT;
data->exposure = PAC207_EXPOSURE_DEFAULT;
data->gain = PAC207_GAIN_DEFAULT;
data->autogain = 1;
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 usbvideo2_device* cam = fh;
struct pac207_data *data = cam->cam_data;
switch (a->id) {
case V4L2_CID_BRIGHTNESS:
a->value = data->brightness;
break;
case V4L2_CID_EXPOSURE:
a->value = data->exposure;
break;
case V4L2_CID_AUTOGAIN:
a->value = data->autogain;
break;
case V4L2_CID_GAIN:
a->value = data->gain;
break;
default:
return -EINVAL;
}
return 0;
}
static int
pac207_vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a)
{
struct usbvideo2_device* cam = fh;
struct pac207_data *data = cam->cam_data;
int new_value = a->value;
int err;
if ((err = pac207_vidioc_g_ctrl(file, fh, a)))
return err;
if (a->value == new_value)
return 0;
/* don't allow mucking with gain / exposure when using autogain */
if (data->autogain && (a->id == V4L2_CID_GAIN ||
a->id == V4L2_CID_EXPOSURE))
return -EINVAL;
switch (a->id) {
case V4L2_CID_BRIGHTNESS:
data->brightness = new_value;
pac207_write_reg(cam, 0x0008, data->brightness);
/* give brightness change time to take effect before
doing autogain based on the new brightness */
data->autogain_ignore_frames =
PAC207_AUTOGAIN_IGNORE_FRAMES;
break;
case V4L2_CID_EXPOSURE:
data->exposure = new_value;
pac207_write_reg(cam, 0x0002, data->exposure);
break;
case V4L2_CID_AUTOGAIN:
data->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 (data->autogain) {
data->exposure = PAC207_EXPOSURE_DEFAULT;
data->gain = PAC207_GAIN_DEFAULT;
data->autogain_ignore_frames =
PAC207_AUTOGAIN_IGNORE_FRAMES;
pac207_write_reg(cam, 0x0002, data->exposure);
pac207_write_reg(cam, 0x000e, data->gain);
}
break;
case V4L2_CID_GAIN:
data->gain = new_value;
pac207_write_reg(cam, 0x000e, data->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 */
return 0;
}
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 usbvideo2_device* cam = fh;
struct pac207_data *data = cam->cam_data;
memcpy(&(f->fmt.pix), &pac207_pix_fmt[data->mode],
sizeof(pac207_pix_fmt[0]));
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 usbvideo2_device* cam = fh;
struct pac207_data *data = cam->cam_data;
if (f->fmt.pix.width == pac207_pix_fmt[0].width) {
data->mode = 0;
cam->imagesize = 352 * 288;
} else {
data->mode = 1;
cam->imagesize = 176 * 144;
}
return 0;
}
/*****************************************************************************/
static int
pac207_probe(struct usbvideo2_device* cam, const struct usb_device_id* id)
{
u8 idreg[2];
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", idreg[0],
idreg[1]);
if (idreg[0] != 0x27) {
DBG(1, "Error invalid sensor ID!");
return -ENODEV;
}
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 */
DBG(2, "Pixart PAC207BCA Image Processor and Control Chip detected "
"(vid/pid 0x%04X:0x%04X)",id->idVendor, id->idProduct);
cam->imagesize = 352 * 288;
strcpy(cam->driver, PAC207_NAME);
strcpy(cam->name, "Pixart PAC207BCA USB Camera");
cam->sof_marker_size = sizeof(pac207_sof_marker);
cam->endpoint_address = 0x85;
return 0;
}
const struct usbvideo2_cam_funcs pac207_funcs = {
.owner = THIS_MODULE,
.probe = pac207_probe,
.find_sof = pac207_find_sof,
.decode_frame_init = pac207_decode_frame_init,
.decode_frame_data = pac207_decode,
.start_transfer = pac207_start_transfer,
.stop_transfer_pre_kill_urbs = pac207_stop_transfer_pre_kill_urbs,
.open = pac207_open,
.frame_dequeued = pac207_do_auto_gain,
.vidioc_query_ctrl = pac207_vidioc_query_ctrl,
.vidioc_s_ctrl = pac207_vidioc_s_ctrl,
.vidioc_g_ctrl = pac207_vidioc_g_ctrl,
.vidioc_enum_fmt_cap = pac207_vidioc_enum_fmt_cap,
.vidioc_g_fmt_cap = pac207_vidioc_g_fmt_cap,
.vidioc_try_fmt_cap = pac207_vidioc_try_fmt_cap,
.vidioc_s_fmt_cap = pac207_vidioc_s_fmt_cap,
};
static int
pac207_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
{
return usbvideo2_probe(intf, id, &pac207_funcs,
sizeof(struct pac207_data));
}
static struct usb_driver pac207_usb_driver = {
.name = PAC207_NAME,
.id_table = pac207_id_table,
.probe = pac207_usb_probe,
.disconnect = usbvideo2_disconnect,
};
/*****************************************************************************/
static void pac207_init_decompress_table(void)
{
int i;
u8 is_abs, len;
s8 val;
for (i = 0; i < 256; i++) {
is_abs = 0;
val = 0;
len = 0;
if ((i & 0xC0) == 0) {
/* code 00 */
val = 0;
len = 2;
} else if ((i & 0xC0) == 0x40) {
/* code 01 */
val = -5;
len = 2;
} else if ((i & 0xC0) == 0x80) {
/* code 10 */
val = +5;
len = 2;
} else if ((i & 0xF0) == 0xC0) {
/* code 1100 */
val = -10;
len = 4;
} else if ((i & 0xF0) == 0xD0) {
/* code 1101 */
val = +10;
len = 4;
} else if ((i & 0xF8) == 0xE0) {
/* code 11100 */
val = -15;
len = 5;
} else if ((i & 0xF8) == 0xE8) {
/* code 11101 */
val = +15;
len = 5;
} else if ((i & 0xFC) == 0xF0) {
/* code 111100 */
val = -20;
len = 6;
} else if ((i & 0xFC) == 0xF4) {
/* code 111101 */
val = +20;
len = 6;
} else if ((i & 0xF8) == 0xF8) {
/* code 11111xxxxxx */
is_abs = 1;
val = 0;
len = 5;
}
pac207_decompress_table[i].is_abs = is_abs;
pac207_decompress_table[i].val = val;
pac207_decompress_table[i].len = len;
}
}
/*****************************************************************************/
static int __init pac207_module_init(void)
{
int err = 0;
pac207_init_decompress_table();
if ((err = usb_register(&pac207_usb_driver)))
printk(KERN_ERR PAC207_NAME
": usb_register() failed, error: %d", err);
return err;
}
static void __exit pac207_module_exit(void)
{
usb_deregister(&pac207_usb_driver);
}
module_init(pac207_module_init);
module_exit(pac207_module_exit);
[-- Attachment #5: usbvideo2.c --]
[-- Type: text/plain, Size: 28862 bytes --]
/* Video4Linux2 framework for usb video devices (webcams) *
* *
* Copyright (C) 2008 by Hans de Goede <j.w.r.degoede@hhs.nl> *
* Buffer management code taken from the Video4Linux2 zc030x driver: *
* Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> *
* Many ideas and code snippets taken from the gspca driver: *
* Copyleft (C) 2005 Michel Xhaard mxhaard@magic.fr *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
***************************************************************************/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/param.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/compiler.h>
#include <linux/ioctl.h>
#include <linux/poll.h>
#include <linux/stat.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
#include <linux/page-flags.h>
#include <linux/byteorder/generic.h>
#include <asm/page.h>
#include <asm/uaccess.h>
#include "usbvideo2.h"
/*****************************************************************************/
MODULE_AUTHOR("Hans de Goede <j.w.r.degoede@hhs.nl>");
MODULE_DESCRIPTION("Video4Linux2 framework for usb video devices (webcams)");
MODULE_LICENSE("GPL");
static unsigned short debug = USBVIDEO2_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(USBVIDEO2_DEBUG_LEVEL)"."
"\n");
/*****************************************************************************/
static DEFINE_MUTEX(usbvideo2_dev_lock);
/*****************************************************************************/
static u32
usbvideo2_request_buffers(struct usbvideo2_device* cam, u32 count)
{
void* buff = NULL;
u32 i;
if (count > USBVIDEO2_MAX_FRAMES)
count = USBVIDEO2_MAX_FRAMES;
cam->nbuffers = count;
while (cam->nbuffers) {
if ((buff = vmalloc_32_user(cam->nbuffers *
PAGE_ALIGN(cam->imagesize))))
break;
cam->nbuffers--;
}
for (i = 0; i < cam->nbuffers; i++) {
cam->frame[i].bufmem = buff + i*PAGE_ALIGN(cam->imagesize);
cam->frame[i].buf.index = i;
cam->frame[i].buf.m.offset = i*PAGE_ALIGN(cam->imagesize);
cam->frame[i].buf.length = cam->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 usbvideo2_release_buffers(struct usbvideo2_device* cam)
{
if (cam->nbuffers) {
vfree(cam->frame[0].bufmem);
cam->nbuffers = 0;
}
}
static void usbvideo2_empty_framequeues(struct usbvideo2_device* cam)
{
u32 i;
INIT_LIST_HEAD(&cam->inqueue);
INIT_LIST_HEAD(&cam->outqueue);
for (i = 0; i < USBVIDEO2_MAX_FRAMES; i++) {
cam->frame[i].state = F_UNUSED;
cam->frame[i].buf.bytesused = 0;
}
}
static void usbvideo2_queue_unusedframes(struct usbvideo2_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);
}
}
/*****************************************************************************/
static void usbvideo2_urb_complete(struct urb *urb)
{
struct usbvideo2_device* cam = urb->context;
struct usbvideo2_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 usbvideo2_frame_t,
frame);
}
for (i = 0; i < urb->number_of_packets; i++) {
unsigned int len, repeat = 0;
u8 *pos, *sof;
len = urb->iso_frame_desc[i].actual_length;
pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer;
if (urb->iso_frame_desc[i].status) {
DBG(3, "Error in isochronous frame");
(*f)->state = F_ERROR;
continue;
}
while (len) {
sof = cam->funcs->find_sof(cam, pos, len, repeat++);
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);
cam->funcs->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 > cam->sof_marker_size)
n -= cam->sof_marker_size;
else
n = 0;
} else
n = len;
if ((ret = cam->funcs->decode_frame_data(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 usbvideo2_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 usbvideo2_kill_and_free_urbs(struct usbvideo2_device* cam)
{
int i;
struct urb* urb;
struct usb_device *udev = cam->usbdev;
for (i = 0; i < USBVIDEO2_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 usbvideo2_stop_transfer(struct usbvideo2_device* cam);
static int usbvideo2_start_transfer(struct usbvideo2_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;
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 ==
cam->endpoint_address)
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 < USBVIDEO2_URBS; i++) {
urb = usb_alloc_urb(USBVIDEO2_ISO_PACKETS, GFP_KERNEL);
if (!urb) {
DBG(1, "usb_alloc_urb() failed");
usbvideo2_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) *
USBVIDEO2_ISO_PACKETS,
GFP_KERNEL,
&urb->transfer_dma);
if (!urb->transfer_buffer) {
DBG(1, "usb_buffer_alloc() failed");
usbvideo2_kill_and_free_urbs(cam);
return -ENOMEM;
}
urb->dev = udev;
urb->context = cam;
urb->pipe = usb_rcvisocpipe(udev,
cam->endpoint_address);
urb->transfer_flags = URB_ISO_ASAP |
URB_NO_TRANSFER_DMA_MAP;
urb->number_of_packets = USBVIDEO2_ISO_PACKETS;
urb->complete = usbvideo2_urb_complete;
urb->transfer_buffer_length = (psz + 1) *
USBVIDEO2_ISO_PACKETS;
urb->interval = 1;
for (j = 0; j < USBVIDEO2_ISO_PACKETS; j++) {
urb->iso_frame_desc[j].offset = (psz + 1) * j;
urb->iso_frame_desc[j].length = psz;
}
}
err = cam->funcs->start_transfer(cam);
if (err) {
DBG(1, "camera specific start_transfer() failed");
usbvideo2_kill_and_free_urbs(cam);
return err;
}
cam->stream = STREAM_ON;
DBG(3, "Stream on");
for (i = 0; i < USBVIDEO2_URBS; i++) {
err = usb_submit_urb(cam->urb[i], GFP_KERNEL);
if (err) {
DBG(1, "usb_submit_urb() failed, error %d",
err);
usbvideo2_stop_transfer(cam);
break;
}
}
}
return err;
}
static void usbvideo2_stop_transfer(struct usbvideo2_device* cam)
{
struct usb_device *udev = cam->usbdev;
if (cam->stream == STREAM_OFF)
return;
if (cam->funcs->stop_transfer_pre_kill_urbs)
cam->funcs->stop_transfer_pre_kill_urbs(cam);
usbvideo2_kill_and_free_urbs(cam);
if (!cam->disconnected) {
int err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */
if (err)
DBG(1, "usb_set_interface( 0 ) error: %d\n", err);
}
if (cam->funcs->stop_transfer_post_kill_urbs)
cam->funcs->stop_transfer_post_kill_urbs(cam);
cam->frame_current = NULL;
cam->stream = STREAM_OFF;
DBG(3, "Stream off");
}
/*****************************************************************************/
static void usbvideo2_release_resources(struct kref *kref)
{
struct usbvideo2_device *cam = container_of(kref,
struct usbvideo2_device, kref);
kfree(cam);
}
static int usbvideo2_open(struct inode* inode, struct file* filp)
{
struct usbvideo2_device* cam;
int err = 0;
/* usb driver disconnect may be running and waiting in
video_unregister_device() for the videodev_lock (from videodev.c),
which we hold when being called! */
if (!mutex_trylock(&usbvideo2_dev_lock))
return -EAGAIN;
cam = video_get_drvdata(video_devdata(filp));
if (cam->users) {
err = -EBUSY;
goto out;
}
if (!try_module_get(cam->funcs->owner)) {
/* Our cam specific module is being removed ! */
err = -ENODEV;
goto out;
}
kref_get(&cam->kref);
filp->private_data = cam;
cam->users++;
cam->io = IO_NONE;
cam->nreadbuffers = USBVIDEO2_DEFAULT_READBUFFERS;
cam->frame_count = 0;
/* init the frame queues */
usbvideo2_empty_framequeues(cam);
err = cam->funcs->open(cam);
if (err) {
DBG(1, "Cam specific open() failed");
module_put(cam->funcs->owner);
kref_put(&cam->kref, usbvideo2_release_resources);
}
out:
mutex_unlock(&usbvideo2_dev_lock);
return err;
}
static int usbvideo2_release(struct inode* inode, struct file* filp)
{
struct usbvideo2_device* cam;
mutex_lock(&usbvideo2_dev_lock);
cam = filp->private_data;
usbvideo2_stop_transfer(cam);
usbvideo2_release_buffers(cam);
cam->users--;
module_put(cam->funcs->owner);
kref_put(&cam->kref, usbvideo2_release_resources);
mutex_unlock(&usbvideo2_dev_lock);
return 0;
}
static ssize_t
usbvideo2_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
{
struct usbvideo2_device *cam = filp->private_data;
struct usbvideo2_frame_t *f;
unsigned long lock_flags;
long timeout;
int err = 0;
if (cam->disconnected) {
err = -ENODEV;
goto out;
}
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 (!usbvideo2_request_buffers(cam, cam->nreadbuffers)) {
DBG(1, "read() failed, not enough memory");
err = -ENOMEM;
goto out;
}
usbvideo2_queue_unusedframes(cam);
if ((err = usbvideo2_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);
usbvideo2_queue_unusedframes(cam);
}
if (!count)
goto out;
/* loop until we get a frame or error, the frame we've been waiting
for may be stolen from underneed us, as we release the lock while
waiting */
while (list_empty(&cam->outqueue)) {
if (filp->f_flags & O_NONBLOCK) {
err = -EAGAIN;
goto out;
}
mutex_unlock(&cam->fileop_mutex);
timeout = wait_event_interruptible_timeout(cam->wait_frame,
!list_empty(&cam->outqueue) ||
cam->disconnected,
msecs_to_jiffies(USBVIDEO2_FRAME_TIMEOUT) );
if (mutex_lock_interruptible(&cam->fileop_mutex))
return -ERESTARTSYS;
if (cam->disconnected) {
err = -ENODEV;
goto out;
}
if (timeout <= 0) {
err = (timeout < 0)? timeout : -EIO;
goto out;
}
}
if (cam->funcs->frame_dequeued)
cam->funcs->frame_dequeued(cam);
spin_lock_irqsave(&cam->queue_lock, lock_flags);
f = list_entry(cam->outqueue.next, struct usbvideo2_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;
usbvideo2_queue_unusedframes(cam);
out:
mutex_unlock(&cam->fileop_mutex);
return err ? err : count;
}
static unsigned int usbvideo2_poll(struct file *filp, poll_table *wait)
{
struct usbvideo2_device* cam = filp->private_data;
struct usbvideo2_frame_t* f;
unsigned long lock_flags;
unsigned int mask = 0;
if (mutex_lock_interruptible(&cam->fileop_mutex))
return POLLERR;
if (cam->disconnected) {
mask = POLLERR;
goto out;
}
if (cam->io == IO_NONE) {
if (!usbvideo2_request_buffers(cam, cam->nreadbuffers)) {
DBG(1, "poll() failed, not enough memory");
mask = POLLERR;
goto out;
}
usbvideo2_queue_unusedframes(cam);
if (usbvideo2_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);
usbvideo2_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 usbvideo2_vm_open(struct vm_area_struct* vma)
{
struct usbvideo2_frame_t* f = vma->vm_private_data;
f->vma_use_count++;
}
static void usbvideo2_vm_close(struct vm_area_struct* vma)
{
/* NOTE: buffers are not freed here */
struct usbvideo2_frame_t* f = vma->vm_private_data;
f->vma_use_count--;
}
static struct vm_operations_struct usbvideo2_vm_ops = {
.open = usbvideo2_vm_open,
.close = usbvideo2_vm_close,
};
static int usbvideo2_mmap(struct file* filp, struct vm_area_struct *vma)
{
struct usbvideo2_device* cam = filp->private_data;
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 (cam->disconnected) {
err = -ENODEV;
goto out;
}
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 = &usbvideo2_vm_ops;
vma->vm_private_data = &cam->frame[i];
usbvideo2_vm_open(vma);
out:
mutex_unlock(&cam->fileop_mutex);
return err;
}
static int usbvideo2_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
struct usbvideo2_device* cam = filp->private_data;
int err;
if (mutex_lock_interruptible(&cam->fileop_mutex))
return -ERESTARTSYS;
if (cam->disconnected) {
err = -ENODEV;
goto out;
}
err = video_ioctl2(inode, filp, cmd, arg);
out:
mutex_unlock(&cam->fileop_mutex);
return err;
}
static const struct file_operations usbvideo2_fops = {
.owner = THIS_MODULE,
.open = usbvideo2_open,
.release = usbvideo2_release,
.ioctl = usbvideo2_ioctl,
.compat_ioctl = v4l_compat_ioctl32,
.read = usbvideo2_read,
.poll = usbvideo2_poll,
.mmap = usbvideo2_mmap,
.llseek = no_llseek,
};
/*****************************************************************************/
static int
usbvideo2_vidioc_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
{
struct usbvideo2_device* cam = fh;
cap->version = LINUX_VERSION_CODE;
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
V4L2_CAP_STREAMING;
strcpy(cap->driver, cam->driver);
strlcpy(cap->card, cam->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
usbvideo2_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
usbvideo2_vidioc_g_input(struct file *file, void *fh, unsigned int *i)
{
*i = 0;
return 0;
}
static int
usbvideo2_vidioc_s_input(struct file *file, void *fh, unsigned int i)
{
if (i != 0)
return -EINVAL;
return 0;
}
static int
usbvideo2_vidioc_s_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
{
struct usbvideo2_device* cam = fh;
int err;
if ((err = cam->funcs->vidioc_try_fmt_cap(file, fh, f)))
return err;
if (cam->stream != STREAM_OFF)
return -EBUSY;
return cam->funcs->vidioc_s_fmt_cap(file, fh, f);
}
static int
usbvideo2_vidioc_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *b)
{
u32 i;
struct usbvideo2_device* cam = fh;
if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
b->memory != V4L2_MEMORY_MMAP)
return -EINVAL;
if (cam->io == IO_READ) {
DBG(2, "Close and open the device again to choose the "
"mmap I/O method");
return -EBUSY;
}
if (cam->stream == STREAM_ON)
return -EBUSY;
for (i = 0; i < cam->nbuffers; i++)
if (cam->frame[i].vma_use_count) {
DBG(2, "VIDIOC_REQBUFS failed. "
"Previous buffers are still mapped.");
return -EBUSY;
}
usbvideo2_release_buffers(cam);
if (b->count)
b->count = usbvideo2_request_buffers(cam, b->count);
cam->io = b->count ? IO_MMAP : IO_NONE;
return 0;
}
static int
usbvideo2_vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
{
struct usbvideo2_device* cam = fh;
if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
b->index >= cam->nbuffers || cam->io != IO_MMAP)
return -EINVAL;
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;
return 0;
}
static int
usbvideo2_vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
{
struct usbvideo2_device* cam = fh;
unsigned long lock_flags;
if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
b->index >= cam->nbuffers ||
cam->io != IO_MMAP ||
cam->frame[b->index].state != F_UNUSED)
return -EINVAL;
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);
return 0;
}
static int
usbvideo2_vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
{
struct usbvideo2_device* cam = fh;
struct usbvideo2_frame_t *f;
unsigned long lock_flags;
long timeout;
if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP ||
cam->stream == STREAM_OFF)
return -EINVAL;
if (list_empty(&cam->outqueue)) {
if (file->f_flags & O_NONBLOCK)
return -EAGAIN;
timeout = wait_event_interruptible_timeout(cam->wait_frame,
!list_empty(&cam->outqueue) ||
cam->disconnected,
msecs_to_jiffies(USBVIDEO2_FRAME_TIMEOUT) );
if (cam->disconnected)
return -ENODEV;
if (timeout <= 0)
return (timeout < 0)? timeout : -EIO;
}
if (cam->funcs->frame_dequeued)
cam->funcs->frame_dequeued(cam);
spin_lock_irqsave(&cam->queue_lock, lock_flags);
f = list_entry(cam->outqueue.next, struct usbvideo2_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;
return 0;
}
static int
usbvideo2_vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
{
struct usbvideo2_device* cam = fh;
if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
return -EINVAL;
return usbvideo2_start_transfer(cam);
}
static int
usbvideo2_vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
{
struct usbvideo2_device* cam = fh;
if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
return -EINVAL;
usbvideo2_stop_transfer(cam);
usbvideo2_empty_framequeues(cam);
return 0;
}
static int
usbvideo2_vidioc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
{
struct usbvideo2_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
usbvideo2_vidioc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
{
struct usbvideo2_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 > USBVIDEO2_MAX_FRAMES)
a->parm.capture.readbuffers = USBVIDEO2_MAX_FRAMES;
cam->nreadbuffers = a->parm.capture.readbuffers;
return 0;
}
/*****************************************************************************/
int
usbvideo2_probe(struct usb_interface* intf, const struct usb_device_id* id,
const struct usbvideo2_cam_funcs *funcs, int cam_specific_data_size)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct usbvideo2_device* cam;
int err = -ENOMEM;
if (!(cam = kzalloc(sizeof(struct usbvideo2_device), GFP_KERNEL)))
return -ENOMEM;
if (!(cam->control_buffer = kzalloc(4, GFP_KERNEL)))
goto fail;
if (!(cam->cam_data = kzalloc(cam_specific_data_size, GFP_KERNEL)))
goto fail;
if (!(cam->v4ldev = video_device_alloc())) {
DBG(1, "video_device_alloc() failed");
goto fail;
}
cam->usbdev = udev;
cam->funcs = funcs;
cam->debug = debug;
if ((err = funcs->probe(cam, id))) {
DBG(1, "Error cam initialization failed");
goto fail;
}
cam->v4ldev->owner = THIS_MODULE;
cam->v4ldev->fops = &usbvideo2_fops;
cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES;
cam->v4ldev->release = video_device_release;
/* cam->v4ldev->debug = V4L2_DEBUG_IOCTL_ARG; */
cam->v4ldev->vidioc_querycap = usbvideo2_vidioc_querycap;
cam->v4ldev->vidioc_enum_input = usbvideo2_vidioc_enuminput;
cam->v4ldev->vidioc_g_input = usbvideo2_vidioc_g_input;
cam->v4ldev->vidioc_s_input = usbvideo2_vidioc_s_input;
cam->v4ldev->vidioc_queryctrl = funcs->vidioc_query_ctrl;
cam->v4ldev->vidioc_g_ctrl = funcs->vidioc_g_ctrl;
cam->v4ldev->vidioc_s_ctrl = funcs->vidioc_s_ctrl;
cam->v4ldev->vidioc_enum_fmt_cap = funcs->vidioc_enum_fmt_cap;
cam->v4ldev->vidioc_g_fmt_cap = funcs->vidioc_g_fmt_cap;
cam->v4ldev->vidioc_s_fmt_cap = usbvideo2_vidioc_s_fmt_cap;
cam->v4ldev->vidioc_try_fmt_cap = funcs->vidioc_try_fmt_cap;
cam->v4ldev->vidioc_reqbufs = usbvideo2_vidioc_reqbufs;
cam->v4ldev->vidioc_querybuf = usbvideo2_vidioc_querybuf;
cam->v4ldev->vidioc_qbuf = usbvideo2_vidioc_qbuf;
cam->v4ldev->vidioc_dqbuf = usbvideo2_vidioc_dqbuf;
cam->v4ldev->vidioc_streamon = usbvideo2_vidioc_streamon;
cam->v4ldev->vidioc_streamoff = usbvideo2_vidioc_streamoff;
cam->v4ldev->vidioc_g_parm = usbvideo2_vidioc_g_parm;
cam->v4ldev->vidioc_s_parm = usbvideo2_vidioc_s_parm;
strlcpy(cam->v4ldev->name, cam->name, sizeof(cam->v4ldev->name));
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:
kfree(cam->control_buffer);
kfree(cam->cam_data);
if (cam->v4ldev)
video_device_release(cam->v4ldev);
kfree(cam);
return err;
}
EXPORT_SYMBOL_GPL(usbvideo2_probe);
void usbvideo2_disconnect(struct usb_interface* intf)
{
struct usbvideo2_device* cam;
mutex_lock(&usbvideo2_dev_lock);
cam = usb_get_intfdata(intf);
DBG(2, "Disconnecting %s...", cam->name);
cam->disconnected = 1;
if (cam->users) {
/* The cam device is still open by some app */
wake_up_interruptible(&cam->wait_frame);
/* Take the fileop_mutex:
1) Because we touch shared resources protected by it
2) So that we know any current users of the usbdev / v4ldev
will be done, there won't come any new ones as we've set
the disconnected flag */
mutex_lock(&cam->fileop_mutex);
usbvideo2_stop_transfer(cam);
mutex_unlock(&cam->fileop_mutex);
}
DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor);
video_set_drvdata(cam->v4ldev, NULL);
video_unregister_device(cam->v4ldev);
cam->v4ldev = NULL;
usb_put_dev(cam->usbdev);
cam->usbdev = NULL;
kfree(cam->control_buffer);
kfree(cam->cam_data);
cam->control_buffer = NULL;
cam->cam_data = NULL;
kref_put(&cam->kref, usbvideo2_release_resources);
mutex_unlock(&usbvideo2_dev_lock);
}
EXPORT_SYMBOL_GPL(usbvideo2_disconnect);
[-- Attachment #6: usbvideo2.h --]
[-- Type: text/plain, Size: 10507 bytes --]
/* Video4Linux2 framework for usb video devices (webcams) *
* *
* Copyright (C) 2008 by Hans de Goede <j.w.r.degoede@hhs.nl> *
* Buffer management code taken from the Video4Linux2 zc030x driver: *
* Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> *
* Many ideas and code snippets taken from the gspca driver: *
* Copyleft (C) 2005 Michel Xhaard mxhaard@magic.fr *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
***************************************************************************/
#ifndef _USBVIDEO2_H_
#define _USBVIDEO2_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 USBVIDEO2_DEBUG_LEVEL 2
#define USBVIDEO2_MAX_FRAMES 32
#define USBVIDEO2_URBS 4 /* 2 is problematic on some systems */
#define USBVIDEO2_ISO_PACKETS 16 /* gspca uses 16 */
#define USBVIDEO2_CTRL_TIMEOUT 100 /* ms */
#define USBVIDEO2_FRAME_TIMEOUT 2000 /* ms */
#define USBVIDEO2_DEFAULT_READBUFFERS 3
/*****************************************************************************/
struct usbvideo2_frame_t;
struct usbvideo2_device;
/*****************************************************************************/
/* How to write a cam specific driver:
1) Implement the below functions (except for those marked optional)
2) fill an instance of the struct below with pointers to your implementation
3) Add an usb device id table to your module
4) Add a simple usb probe function which simply calls the usbvideo2_probe
function (further below), passing in your cam specific functions and
the sizeof your cam specific data structure (usbvideo2 will kzalloc and
free it for you)
5) Let the disconnect member of your usb_driver struct point to
usbvideo2_disconnect
*/
/* Cam specific functions structure */
struct usbvideo2_cam_funcs {
/* set this to THIS_MODULE */
struct module *owner;
/* This function gets called from usbvideo2_probe() when a new device
matching any of the listed usb ID's is connnected / inserted. This
function should:
1) Check if the device is supported by the cam specific code
2) initialize the device (turn of led, etc.)
3) Set the cam specific data in the usbvideo2_device (which data
is cam specific is detailed in the struct declaration)
4) initialize cam->cam_data as needed, it is already allocated
by usbvideo2_probe() when this function gets called,
one just needs to fill in any non 0 values */
int (*probe) (struct usbvideo2_device* cam,
const struct usb_device_id* id);
/* Note the 3 sof / decode functions below are called from the urb
completion handler and thus must not sleep! No locks are held when
they are called! */
/* Should return NULL when no sof, otherwise a pointer to the first
byte after the SOF marker, the repeat param gets set to 0 the first
call for one iso packet, to 1 the second call within the same iso
packet, etc. This can be used for optimalization when the cam driver
knows that there can be only one sof in a frame */
u8 * (*find_sof) (struct usbvideo2_device* cam, u8* data,
unsigned int len, int repeat);
/* This function gets called before the decode_frame_data() gets called
for the first time after a sof has been detected, this can be used
to reset the decoder state */
void (*decode_frame_init) (struct usbvideo2_device* cam);
/* This function gets called when there is iso packet data to decode
the first time it gets called after a call to decode_frame_init()
the data pointer will point to the first byte after the sof header,
iow to the byte returned by find_sof().
This function should return 1 if a frame was completely written to
f->buf, in all other circumstances it should return 0. It can signal
an error in decoding by setting f->state to F_ERROR, and when
returning 1 it should also set f->state to F_DONE. */
int (*decode_frame_data) (struct usbvideo2_device* cam,
struct usbvideo2_frame_t *f, u8 *data, unsigned int len);
/* Note all functions below are called with the fileop_mutex locked
and thus you can be sure only one of them will be called at a time.
Notice that the sof / frame decode functions above may still run
simultaniously though! */
/* Start transfer: activate sampling and start up iso endpoint,
return 0 on success, otherwise a negative errorcode */
int (*start_transfer) (struct usbvideo2_device* cam);
/* Stop transfer: stop sampling and shut down iso endpoint.
This version of stop_transfer gets called _before_ the urbs
currently in progress are killed. This function should check
cam->disconnected before doing any usb I/O, its still called on
a disconnected webcam to free any resources claimed by
start_transfer() (optional). */
void (*stop_transfer_pre_kill_urbs) (struct usbvideo2_device* cam);
/* Like stop_transfer_pre_kill_urbs, but gets called _after_ the urbs
currently in progress are killed (optional). */
void (*stop_transfer_post_kill_urbs) (struct usbvideo2_device* cam);
/* Open: called when the /dev/videoX device gets opened this function
should reset things like contrast and brightness, etc. Back to their
defaults. return 0 on success, otherwise a negative errorcode */
int (*open) (struct usbvideo2_device* cam);
/* Post frame dequeue (or read) callback, usefull todo things like
semi software auto exposure / gain (optional) */
void (*frame_dequeued) (struct usbvideo2_device* cam);
/* Below are standard v4l2 ioctl handlers */
int (*vidioc_query_ctrl) (struct file *file, void *fh,
struct v4l2_queryctrl *a);
int (*vidioc_g_ctrl) (struct file *file, void *fh,
struct v4l2_control *a);
int (*vidioc_s_ctrl) (struct file *file, void *fh,
struct v4l2_control *a);
int (*vidioc_enum_fmt_cap) (struct file *file, void *fh,
struct v4l2_fmtdesc *f);
int (*vidioc_g_fmt_cap) (struct file *file, void *fh,
struct v4l2_format *f);
int (*vidioc_try_fmt_cap) (struct file *file, void *fh,
struct v4l2_format *f);
/* Note some sanity checks (try_fmt_cap, not streaming) are already
done when this gets called */
int (*vidioc_s_fmt_cap) (struct file *file, void *fh,
struct v4l2_format *f);
};
/* Use as explained above in "How to write a cam specific driver" */
int usbvideo2_probe(struct usb_interface* intf, const struct usb_device_id* id,
const struct usbvideo2_cam_funcs *funcs, int cam_specific_data_size);
void usbvideo2_disconnect(struct usb_interface* intf);
/*****************************************************************************/
enum usbvideo2_frame_state {
F_UNUSED,
F_QUEUED,
F_GRABBING,
F_DONE,
F_ERROR,
};
struct usbvideo2_frame_t {
void* bufmem;
struct v4l2_buffer buf;
enum usbvideo2_frame_state state;
struct list_head frame;
unsigned long vma_use_count;
};
enum usbvideo2_io_method {
IO_NONE,
IO_READ,
IO_MMAP,
};
enum usbvideo2_stream_state {
STREAM_OFF,
STREAM_ON,
};
struct usbvideo2_device {
const struct usbvideo2_cam_funcs *funcs;
void *cam_data; /* cam specific functions private data */
struct usb_device* usbdev;
/* 4 kmalloc-ed bytes for cam read / write register methods */
u8* control_buffer;
/* These should not be touched by the cam specific code */
struct video_device* v4ldev;
struct urb* urb[USBVIDEO2_URBS];
struct usbvideo2_frame_t *frame_current, frame[USBVIDEO2_MAX_FRAMES];
struct list_head inqueue, outqueue;
struct kref kref;
struct mutex fileop_mutex;
spinlock_t queue_lock;
wait_queue_head_t wait_frame;
u32 frame_count, nbuffers, nreadbuffers;
/* End usbvideo2 core private data */
/* These should be initialised by the cam specific code in its probe
function and be updated as necessary */
u32 imagesize;
char driver[16];
char name[32];
u8 sof_marker_size;
u8 endpoint_address;
/* end cam specific data */
/* Thse should only be read by the cam specific code */
u8 debug;
u8 disconnected;
/* These should not be touched by the cam specific code */
u8 users;
u8 io;
u8 stream;
};
/*****************************************************************************/
#undef DBG
#define DBG(level, fmt, args...) \
do { \
if (cam->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 /* _USBVIDEO2_H_ */
[-- Attachment #7: Type: text/plain, Size: 164 bytes --]
--
video4linux-list mailing list
Unsubscribe mailto:video4linux-list-request@redhat.com?subject=unsubscribe
https://www.redhat.com/mailman/listinfo/video4linux-list
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [New Driver]: usbvideo2 webcam core + pac207 driver using it.
2008-03-28 21:53 [New Driver]: usbvideo2 webcam core + pac207 driver using it Hans de Goede
@ 2008-04-03 21:27 ` Brandon Philips
2008-04-04 7:00 ` Hans de Goede
[not found] ` <20080404144935.3b457c69.zaitcev@redhat.com>
[not found] ` <20080404184855.963369c5.zaitcev@redhat.com>
1 sibling, 2 replies; 7+ messages in thread
From: Brandon Philips @ 2008-04-03 21:27 UTC (permalink / raw)
To: Hans de Goede; +Cc: fedora-kernel-list, video4linux-list, spca50x-devs
On 22:53 Fri 28 Mar 2008, Hans de Goede wrote:
> 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.
The driver look pretty good. Comments inline.
> struct pac207_decompress_table_t {
> u8 is_abs;
> u8 len;
> s8 val;
> };
Why add the _t?
> int pac207_read_reg(struct usbvideo2_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, USBVIDEO2_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;
Why not do the obvious thing and return from the if (res < 0) statement?
> /*****************************************************************************/
>
> /* auto gain and exposure algorithm based on the knee algorithm described here:
> http://ytse.tricolour.net/docs/LowLightOptimization.html */
URL is dead.
> #define CLIP(color) (unsigned char)(((color)>0xFF)?0xff:(((color)<0)?0:(color)))
Add a comment about what this is doing? Could you just do it as a
static function instead?
> static int
> pac207_vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a)
> {
> struct usbvideo2_device* cam = fh;
> struct pac207_data *data = cam->cam_data;
> int new_value = a->value;
> int err;
>
> if ((err = pac207_vidioc_g_ctrl(file, fh, a)))
> return err;
>
> if (a->value == new_value)
> return 0;
This all needs some locking to protect from multi-threaded applications.
Otherwise the hardware and data structures could be in two different
states.
> /* don't allow mucking with gain / exposure when using autogain */
> if (data->autogain && (a->id == V4L2_CID_GAIN ||
> a->id == V4L2_CID_EXPOSURE))
> return -EINVAL;
>
> switch (a->id) {
> case V4L2_CID_BRIGHTNESS:
> data->brightness = new_value;
> pac207_write_reg(cam, 0x0008, data->brightness);
> /* give brightness change time to take effect before
> doing autogain based on the new brightness */
> data->autogain_ignore_frames =
> PAC207_AUTOGAIN_IGNORE_FRAMES;
> break;
>
> case V4L2_CID_EXPOSURE:
> data->exposure = new_value;
> pac207_write_reg(cam, 0x0002, data->exposure);
> break;
>
> case V4L2_CID_AUTOGAIN:
> data->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 (data->autogain) {
> data->exposure = PAC207_EXPOSURE_DEFAULT;
> data->gain = PAC207_GAIN_DEFAULT;
> data->autogain_ignore_frames =
> PAC207_AUTOGAIN_IGNORE_FRAMES;
> pac207_write_reg(cam, 0x0002, data->exposure);
> pac207_write_reg(cam, 0x000e, data->gain);
> }
> break;
>
> case V4L2_CID_GAIN:
> data->gain = new_value;
> pac207_write_reg(cam, 0x000e, data->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 */
>
> return 0;
> }
> static void usbvideo2_urb_complete(struct urb *urb)
> {
> struct usbvideo2_device* cam = urb->context;
> struct usbvideo2_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 usbvideo2_frame_t,
> frame);
> }
Don't you want to take a spinlock here? Most accesses of inqueue seem
to take a spinlock.
> static ssize_t
> usbvideo2_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
> {
> struct usbvideo2_device *cam = filp->private_data;
> struct usbvideo2_frame_t *f;
> unsigned long lock_flags;
> long timeout;
> int err = 0;
>
>
> if (cam->disconnected) {
> err = -ENODEV;
> goto out;
> }
>
> if (cam->io == IO_MMAP) {
> DBG(2, "Close and open the device again to choose the read "
> "method");
> err = -EBUSY;
> goto out;
> }
>
> static void usbvideo2_vm_close(struct vm_area_struct* vma)
> {
> /* NOTE: buffers are not freed here */
Why is that worth noting?
> struct usbvideo2_frame_t* f = vma->vm_private_data;
> f->vma_use_count--;
> }
>
>
> static int
> usbvideo2_vidioc_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *b)
> {
> u32 i;
> struct usbvideo2_device* cam = fh;
>
> if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
> b->memory != V4L2_MEMORY_MMAP)
> return -EINVAL;
>
> if (cam->io == IO_READ) {
> DBG(2, "Close and open the device again to choose the "
> "mmap I/O method");
> return -EBUSY;
> }
Again, why is close then open required?
> static int
> usbvideo2_vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
> {
> struct usbvideo2_device* cam = fh;
> struct usbvideo2_frame_t *f;
> unsigned long lock_flags;
> long timeout;
>
> if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP ||
> cam->stream == STREAM_OFF)
> return -EINVAL;
>
> if (list_empty(&cam->outqueue)) {
> if (file->f_flags & O_NONBLOCK)
> return -EAGAIN;
>
> timeout = wait_event_interruptible_timeout(cam->wait_frame,
> !list_empty(&cam->outqueue) ||
> cam->disconnected,
> msecs_to_jiffies(USBVIDEO2_FRAME_TIMEOUT) );
> if (cam->disconnected)
> return -ENODEV;
>
> if (timeout <= 0)
> return (timeout < 0)? timeout : -EIO;
> }
Where is the locking? What happens if two threads call dqbuf on this
device at the same time?
> if (cam->funcs->frame_dequeued)
> cam->funcs->frame_dequeued(cam);
>
> spin_lock_irqsave(&cam->queue_lock, lock_flags);
You will probably hit LIST_POISON on a second thread because of the list
being empty when it comes through.
> f = list_entry(cam->outqueue.next, struct usbvideo2_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;
>
> return 0;
> }
> struct usbvideo2_frame_t {
> void* bufmem;
> struct v4l2_buffer buf;
> enum usbvideo2_frame_state state;
> struct list_head frame;
> unsigned long vma_use_count;
> };
Why the _t on the end?
Thanks,
Brandon
--
video4linux-list mailing list
Unsubscribe mailto:video4linux-list-request@redhat.com?subject=unsubscribe
https://www.redhat.com/mailman/listinfo/video4linux-list
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [New Driver]: usbvideo2 webcam core + pac207 driver using it.
2008-04-03 21:27 ` Brandon Philips
@ 2008-04-04 7:00 ` Hans de Goede
2008-04-04 18:56 ` Brandon Philips
[not found] ` <20080404144935.3b457c69.zaitcev@redhat.com>
1 sibling, 1 reply; 7+ messages in thread
From: Hans de Goede @ 2008-04-04 7:00 UTC (permalink / raw)
To: Brandon Philips; +Cc: video4linux-list, spca50x-devs
Brandon Philips wrote:
> On 22:53 Fri 28 Mar 2008, Hans de Goede wrote:
>> 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.
>
> The driver look pretty good. Comments inline.
>
Thanks for the review!
>> struct pac207_decompress_table_t {
>> u8 is_abs;
>> u8 len;
>> s8 val;
>> };
>
> Why add the _t?
>
So that I can write "struct pac207_decompress_table_t
pac207_decompress_table[256];" further on.
>> int pac207_read_reg(struct usbvideo2_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, USBVIDEO2_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;
>
> Why not do the obvious thing and return from the if (res < 0) statement?
>
Good point.
>> /*****************************************************************************/
>>
>> /* auto gain and exposure algorithm based on the knee algorithm described here:
>> http://ytse.tricolour.net/docs/LowLightOptimization.html */
>
> URL is dead.
>
Huh, you're right it was still alive when I wrote my mail, lets give it some
time to come back (I hope it does).
>> #define CLIP(color) (unsigned char)(((color)>0xFF)?0xff:(((color)<0)?0:(color)))
>
> Add a comment about what this is doing? Could you just do it as a
> static function instead?
>
What it does it obvious isn't it? Sure I could make this into a funciton that
indeed would be somewhat more readable.
>> static int
>> pac207_vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a)
>> {
>> struct usbvideo2_device* cam = fh;
>> struct pac207_data *data = cam->cam_data;
>> int new_value = a->value;
>> int err;
>>
>> if ((err = pac207_vidioc_g_ctrl(file, fh, a)))
>> return err;
>>
>> if (a->value == new_value)
>> return 0;
>
> This all needs some locking to protect from multi-threaded applications.
> Otherwise the hardware and data structures could be in two different
> states.
>
They are all called with the usbvideo2 "core" fileop_mutex lock held, as is
documented in usbvideo2.h
<snip>
>
>> static void usbvideo2_urb_complete(struct urb *urb)
>> {
>> struct usbvideo2_device* cam = urb->context;
>> struct usbvideo2_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 usbvideo2_frame_t,
>> frame);
>> }
>
> Don't you want to take a spinlock here? Most accesses of inqueue seem
> to take a spinlock.
>
Good catch! Note that this bug is present in the current in mainline zc0301,
et61x251, and sn9c102 drivers too!!
(I modelled my driver after these).
<snip>
>> static void usbvideo2_vm_close(struct vm_area_struct* vma)
>> {
>> /* NOTE: buffers are not freed here */
>
> Why is that worth noting?
>
I guess it isn't this was copied verbatim from the zc0301 driver.
>> struct usbvideo2_frame_t* f = vma->vm_private_data;
>> f->vma_use_count--;
>> }
>>
>>
>> static int
>> usbvideo2_vidioc_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *b)
>> {
>> u32 i;
>> struct usbvideo2_device* cam = fh;
>>
>> if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
>> b->memory != V4L2_MEMORY_MMAP)
>> return -EINVAL;
>>
>> if (cam->io == IO_READ) {
>> DBG(2, "Close and open the device again to choose the "
>> "mmap I/O method");
>> return -EBUSY;
>> }
>
> Again, why is close then open required?
>
This whole check is just there to give a hint to the applicaiton writer, that
mixing mmap and read won't work. If a read() call is made, then it will request
a number of buffers, and effectively do a transfer_on ioctl, starting the
stream. If this check is removed, the usbvideo2_vidioc_reqbufs() ioctl will
still fail if done after a read call is made, because the next check will fail:
if (cam->stream == STREAM_ON) {
return -EBUSY;
}
Thic check is mandatory as freeing the buffers will the usb isoc stream is
active is a bad idea! So I can remove the if (cam->io == IO_READ) { check
without any ill effects, its just there to give a hint to application writers
why switching between mmap <-> read will fial most of the time.
>> static int
>> usbvideo2_vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
>> {
>> struct usbvideo2_device* cam = fh;
>> struct usbvideo2_frame_t *f;
>> unsigned long lock_flags;
>> long timeout;
>>
>> if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP ||
>> cam->stream == STREAM_OFF)
>> return -EINVAL;
>>
>> if (list_empty(&cam->outqueue)) {
>> if (file->f_flags & O_NONBLOCK)
>> return -EAGAIN;
>>
>> timeout = wait_event_interruptible_timeout(cam->wait_frame,
>> !list_empty(&cam->outqueue) ||
>> cam->disconnected,
>> msecs_to_jiffies(USBVIDEO2_FRAME_TIMEOUT) );
>> if (cam->disconnected)
>> return -ENODEV;
>>
>> if (timeout <= 0)
>> return (timeout < 0)? timeout : -EIO;
>> }
>
> Where is the locking? What happens if two threads call dqbuf on this
> device at the same time?
>
All ioctl handlers gets called through usbvideo2_ioctl(), which takes the
fileop_mutex, then checks if the device hasn't been disconnected from
underneath us, and then calls video_ioctl2(...). This video_ioctl2() wrapping
is needed to check for disconnected devices, and as it was needed anyway also
was a good central place to add locking, simplifying all the ioctl handlers,
esp. there error exit paths.
>
>> struct usbvideo2_frame_t {
>> void* bufmem;
>> struct v4l2_buffer buf;
>> enum usbvideo2_frame_state state;
>> struct list_head frame;
>> unsigned long vma_use_count;
>> };
>
> Why the _t on the end?
>
No special reason in this case.
Thanks & Regards,
Hans
p.s.
I'm currently trying to merge my work and the work to port gspca as a whole to
v4l2 of Jean-François Moine, so don't expect a new iteration of this soon, as I
first want to have a clear path for merging these 2 works.
--
video4linux-list mailing list
Unsubscribe mailto:video4linux-list-request@redhat.com?subject=unsubscribe
https://www.redhat.com/mailman/listinfo/video4linux-list
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [New Driver]: usbvideo2 webcam core + pac207 driver using it.
2008-04-04 7:00 ` Hans de Goede
@ 2008-04-04 18:56 ` Brandon Philips
0 siblings, 0 replies; 7+ messages in thread
From: Brandon Philips @ 2008-04-04 18:56 UTC (permalink / raw)
To: Hans de Goede; +Cc: video4linux-list, spca50x-devs
On 09:00 Fri 04 Apr 2008, Hans de Goede wrote:
> Brandon Philips wrote:
> > On 22:53 Fri 28 Mar 2008, Hans de Goede wrote:
> >> 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.
> > The driver look pretty good. Comments inline.
>
> Thanks for the review!
>
> >> struct pac207_decompress_table_t {
> >> u8 is_abs;
> >> u8 len;
> >> s8 val;
> >> };
> > Why add the _t?
>
> So that I can write "struct pac207_decompress_table_t
> pac207_decompress_table[256];" further on.
But, why does the struct have a _t on the end of the name? Usually that
is used for typedefs of structs.
> > This all needs some locking to protect from multi-threaded applications.
> > Otherwise the hardware and data structures could be in two different
> > states.
>
> They are all called with the usbvideo2 "core" fileop_mutex lock held, as is
> documented in usbvideo2.h
Oops, I see that now.
> >> static void usbvideo2_urb_complete(struct urb *urb)
> >> {
> >> struct usbvideo2_device* cam = urb->context;
> >> struct usbvideo2_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 usbvideo2_frame_t,
> >> frame);
> >> }
> > Don't you want to take a spinlock here? Most accesses of inqueue seem
> > to take a spinlock.
>
> Good catch! Note that this bug is present in the current in mainline zc0301,
> et61x251, and sn9c102 drivers too!!
>
Ok, I will look at this and submit patches. Thanks.
> I'm currently trying to merge my work and the work to port gspca as a whole
> to v4l2 of Jean-François Moine, so don't expect a new iteration of this
> soon, as I first want to have a clear path for merging these 2 works.
Great. It would be good to get gspca into the Kernel.
Cheers,
Brandon
--
video4linux-list mailing list
Unsubscribe mailto:video4linux-list-request@redhat.com?subject=unsubscribe
https://www.redhat.com/mailman/listinfo/video4linux-list
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [New Driver]: usbvideo2 webcam core + pac207 driver using it.
[not found] ` <20080404144935.3b457c69.zaitcev@redhat.com>
@ 2008-04-05 7:36 ` Hans de Goede
0 siblings, 0 replies; 7+ messages in thread
From: Hans de Goede @ 2008-04-05 7:36 UTC (permalink / raw)
To: Pete Zaitcev; +Cc: fedora-kernel-list, video4linux-list, spca50x-devs
Pete Zaitcev wrote:
>>> #define CLIP(color) (unsigned char)(((color)>0xFF)?0xff:(((color)<0)?0:(color)))
>> Add a comment about what this is doing? Could you just do it as a
>> static function instead?
>
> The macro itself is too trivial to be commented, IMHO, but I have
> to ask just what it is doing there. It is only applied to
> precomputed values from pac207_decompress_table, as far as I see.
> So, they cannot be out of range. Or can they?
>
Its being applied to the addition of a value read from the sensor and a
precomputed value from the pac207_decompress_table, and the total of these can
be out of range.
Regards,
Hans
--
video4linux-list mailing list
Unsubscribe mailto:video4linux-list-request@redhat.com?subject=unsubscribe
https://www.redhat.com/mailman/listinfo/video4linux-list
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [New Driver]: usbvideo2 webcam core + pac207 driver using it.
[not found] ` <20080404184855.963369c5.zaitcev@redhat.com>
@ 2008-04-05 7:52 ` Hans de Goede
0 siblings, 0 replies; 7+ messages in thread
From: Hans de Goede @ 2008-04-05 7:52 UTC (permalink / raw)
To: Pete Zaitcev; +Cc: fedora-kernel-list, video4linux-list, spca50x-devs
Pete Zaitcev wrote:
> On Fri, 28 Mar 2008 22:53:39 +0100, Hans de Goede <j.w.r.degoede@hhs.nl> wrote:
>
>> u8 users;
>
> Make it int, too easy to overflow.
>
The current code doesn't allow simultanious opens, so this can become only 0 or 1 .
>> static u32
>> usbvideo2_request_buffers(struct usbvideo2_device* cam, u32 count)
>> {
>> if ((buff = vmalloc_32_user(cam->nbuffers *
>> PAGE_ALIGN(cam->imagesize))))
>
> I think that placing buffers into the lower 4GB is a senseless
> limitation to build into a library. Although today the only system
> which can run DMA between memory above 4GB and USB is Opteron,
> with XHCI it's going to change.
Actually this isn't used for dma at all, so the _32_ can indeed go away.
>
>> spin_unlock(&cam->queue_lock);
>> wake_up_interruptible(&cam->wait_frame);
>
> Why only interruptible
Because there are only interruptable waits done on it and My Linux Device
Drivers 3th edition (Corbet et All) says to use _interruptible when there are
only interruptible waiters. Also I took most of this from exisiting v4l2 usb
drivers which do it the same.
> , and why outside of the lock? Bizarre.
Why would this need to be done inside the lock? The wait_frame queue has its
own internal locking, no need to keep the queue lock longer then necessary esp
as its a spinlock.
>> urb->dev = cam->usbdev;
>> ret = usb_submit_urb(urb, GFP_ATOMIC);
>
> If you don't plan on using this code on kernel 2.4, don't restore
> urb->dev. This was unnecessary for years. Just set it where you
> fill the URB.
>
Ok.
>> urb->transfer_buffer = usb_buffer_alloc(udev,
>> (psz + 1) *
>> USBVIDEO2_ISO_PACKETS,
>> GFP_KERNEL,
>> &urb->transfer_dma);
>
> Why are you using usb_buffer_alloc here? Why not use kmalloc?
>
I took this from gspca, and I believe this is necessary to get a buffer will at
all the proper attributes for being able todo a dma transfer to it.
> Secondly, let's suppose you're allocating for 512-byte packets
> (actually 513 for some reason). You're above 8KB by a few bytes,
> thus making this an order 2 allocation. I am quite certain this
> is going to fail on loaded systems.
>
Good point!
> If you ever run across a device with a bigger maximum packet size
> (e.g. a WUSB webcam) this is just going to crash and burn.
>
> So, why do you need to stuff 16 ISO blocks into one URB? Hardware
> limitation?
>
No special reason I copied this from gspca, I guess I can just make it
PAGE_SIZE / (packet_size + 1)
> Any why is this affinity to device's declared packet size? The HC
> transparently merges transfers for you. So, the only thing you
> need to think about is if your buffers are an integral number
> of packets.
>
Some webcams are so nice as to only put a sof (start of frame) marker/header at
the start of a packet, so if the packets match actually device packets, then
one only needs to check the first few bytes for the magic sof marker, instead
of having to search through the entire packet (as one must do with other less
nice webcams).
> What is this +1 business, anyway?
>
The pac207 (and sn109c bayer compression code uses variable length bit
patterns, the decoder always reads 8 bits and the looks this up in a table
which tells it amongst other things how much bits where actually used. So if
the last pixel in a row is coded in say 2 bits, and it happens to also be at
the end of a packet 6 additional bits will get read (and ignored).
So the buffer gets allocated 1 byte too large to allow safe reading of these 6
bits.
Regards,
Hans
--
video4linux-list mailing list
Unsubscribe mailto:video4linux-list-request@redhat.com?subject=unsubscribe
https://www.redhat.com/mailman/listinfo/video4linux-list
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2008-04-05 7:53 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-03-28 21:53 [New Driver]: usbvideo2 webcam core + pac207 driver using it Hans de Goede
2008-04-03 21:27 ` Brandon Philips
2008-04-04 7:00 ` Hans de Goede
2008-04-04 18:56 ` Brandon Philips
[not found] ` <20080404144935.3b457c69.zaitcev@redhat.com>
2008-04-05 7:36 ` Hans de Goede
[not found] ` <20080404184855.963369c5.zaitcev@redhat.com>
2008-04-05 7:52 ` Hans de Goede
-- strict thread matches above, loose matches on Subject: below --
2008-03-28 21:52 Hans de Goede
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox