This module does not work as is from the new kernel.
In attachment you have a piece of the source code and the README file with
the email address of the programmers plus the details...
It works now with my Quickcam Express USB!
Regards,
Marc
#ifndef __LINUX_QUICKCAM_H
#define __LINUX_QUICKCAM_H
#include <linux/list.h>
#include <linux/usb.h>
#include <linux/videodev.h>
#define _QUICKCAM_ISOPIPE 0x81
#define SHUTTER_VAL 0x80
#define EXPO_VAL 0xa0
#define RGAIN_DEF 0x80
#define BGAIN_DEF 0x80
#define GGAIN_DEF 0x80
// define debug levels
#define DEBUGBASE 1
#define DEBUGLOGIC 2
#define DEBUGDATA 4
#define DEBUGREAD 8
#define DEBUGIOCTL 16
#define DEBUGINT 32
#define VID_HARDWARE_QCE 50
// Control register of the STV0600 ASIC
#define STV_ISO_ENABLE 0x1440
#define STV_SCAN_RATE 0x1443
#define STV_ISO_SIZE 0x15c1
#define STV_Y_CTRL 0x15c3
#define STV_X_CTRL 0x1680
#define STV_REG00 0x1500
#define STV_REG01 0x1501
#define STV_REG02 0x1502
#define STV_REG03 0x1503
#define STV_REG04 0x1504
#define STV_REG23 0x0423
#define STREAM_BUF_SIZE (PAGE_SIZE * 4)
#define SCRATCH_BUF_SIZE (STREAM_BUF_SIZE * 2)
#define FRAMES_PER_DESC 10
#define FRAME_SIZE_PER_DESC 1023 // Shouldn't be hardcoded JFC was 960
// 356*292 from VV6410 352*288 otherwise.
// max size of frame received from the camera
#define BAYER_FRAME_SIZE 103952
#define QUICKCAM_NUMFRAMES 2
#define QUICKCAM_NUMSBUF 2
/* scanstate */
enum {
STATE_OK, // Ok.
STATE_ERROR, // An error has been detected.
};
/* store state, that to know what to do with the frame */
enum {
FRAME_STORE, // Ok.
FRAME_SKIP, // we have skipped data the frame is partial.
};
/* grabstate (of the frame). */
enum {
FRAME_UNUSED, /* Unused (no MCAPTURE) */
FRAME_READY, /* Ready to start grabbing */
FRAME_GRABBING, /* In the process of being grabbed into */
FRAME_DONE, /* Finished grabbing, but not been synced yet */
FRAME_ERROR, /* Something bad happened while processing */
};
struct usb_device;
struct quickcam_sbuf {
char *data;
urb_t *urb;
};
struct palette_list {
int num;
char *name;
int supported;
};
/*
* Structure filled in for each of the types of sensor (HDCS, PB0100)
*/
struct sensorctrl {
int (*init) (struct usb_device * dev, int mode,
int *rgain, int *bgain, int *ggain,
struct sensorctrl *sensor_ctrl);
int (*set_shutter) (struct usb_device * dev, int sval, int xval);
int (*set_gains) (struct usb_device * dev, int rgain, int bgain, int ggain);
int (*set_window) (struct usb_device * dev, int x, int y, int w, int h,
struct sensorctrl *sensor_ctrl);
int (*set_size) (struct usb_device * dev, int mode);
int (*start) (struct usb_device * dev, struct sensorctrl *sensor_ctrl);
int (*stop) (struct usb_device * dev, struct sensorctrl *sensor_ctrl);
// size delivered by the sensor.
int width;
int height;
// mode: 0 full; 1 half; 2 quater.
int mode;
};
/**
* Structure storing the I2C messages.
* Do use it:
* usb_quickcam_i2c_new reset the structure.
* usb_quickcam_i2c_add add register and value.
* usb_quickcam_i2c_send send it to the sensor. It call a usb_quickcam_i2c_new.
*/
struct quickcam_i2c {
int length;
unsigned char buff[35];
};
struct quickcam_frame {
char *data; /* Frame buffer */
long scanlength; /* uncompressed, raw data length of frame */
char *storedata; /* Bayer data */
long storelength; /* len of received data */
int width; /* Width application is expecting */
int height; /* Height */
// int hdrwidth; /* Width the frame actually is */
// int hdrheight; /* Height */
volatile int grabstate; /* State of grabbing */
long bytes_read; /* amount of scanlength that has been read from *data */
wait_queue_head_t wq; /* Processes waiting */
};
struct usb_quickcam {
struct video_device vdev;
struct video_picture vpic;
struct video_window vwin;
/* Device structure */
struct usb_device *dev;
/* For /proc interface */
struct proc_dir_entry *proc_entry;
unsigned char iface;
struct semaphore lock;
int user; /* user count for exclusive use */
int streaming; /* Are we streaming Isochronous? */
int grabbing; /* Are we grabbing? */
int readframe; /* the frame we are reading. */
int scanstate; /* state of the automaton */
int storestate; /* indicated that we have to wait for the end */
int sizechanged; /* indicated size changes */
int yy;
int x;
int shutter_val;
int gain; /* global gain */
int val; /* global exposure */
int blue; /* blue gain */
int red; /* red gain */
int green;/* green gain */
int brightness;/* Control brightness of image through V4L */
char *sensor_name; /* for /proc */
unsigned char sensor_addr; /* hdcs and photobit have different addr */
char *fbuf; /* Videodev buffer area */
int curframe;
struct quickcam_frame frame[QUICKCAM_NUMFRAMES]; /* Double buffering */
struct quickcam_sbuf sbuf[QUICKCAM_NUMSBUF]; /* Double buffering */
/* Scratch space from the Isochronous pipe */
unsigned char scratch[SCRATCH_BUF_SIZE];
int scratchlen;
struct semaphore busy_lock; /* guard against SMP multithreading */
struct sensorctrl sensor_ctrl; /* specific routines for a sensor */
};
/* sensor informations (input about all supported sensors) */
struct sensor_data {
char *name;
int reg23;
unsigned char i2c_addr;
int id_reg;
unsigned char id;
int length_id;
void (*load)(struct sensorctrl *);
};
/* Add prototyping to prevent warnings */
/* Sensor initialisation */
void load_hdcs_mod(struct sensorctrl *sensor_ctrl);
void load_hdcs20_mod(struct sensorctrl *sensor_ctrl);
void load_pb0100_mod(struct sensorctrl *sensor_ctrl);
void load_vv6410_mod(struct sensorctrl *sensor_ctrl);
/* Sensor commmon routines */
void usb_quickcam_i2c_new(struct quickcam_i2c *i2cbuff);
void usb_quickcam_i2c_add(struct quickcam_i2c *i2cbuff,unsigned char reg,
unsigned char value);
void usb_quickcam_i2c_add2(struct quickcam_i2c *i2cbuff,unsigned char reg,
unsigned short value);
int usb_quickcam_i2c_send(struct usb_device *dev, struct quickcam_i2c *i2cbuff,
unsigned char sensor_add);
/* USB interface chip control */
int usb_quickcam_set1(struct usb_device *dev, short reg, char val);
int usb_quickcam_set2(struct usb_device *dev, short reg, short val);
/* conversion routines */
int IsSupported(int format);
void quickcam_convert_image(struct quickcam_frame *frame, int format);
char *FormatName(int format);
#endif
USB Quickcam Video Camera driver
Supports Logitech Quickcam Express
Quickcam Team:
Lead developer
Jean-Freceric Clere <[EMAIL PROTECTED]>
Nikolas Zimmermann <[EMAIL PROTECTED]>
Quickcam Revers-Enginnering:
Georg Acher <[EMAIL PROTECTED]>
Photobit Support:
Mark Cave-Ayland <[EMAIL PROTECTED]>
Would also like to thank the following contributors:
Carlo E Prelz, Rogier Wolff, Samuel Linclau, Matthew Denner,
and any body else whom we have failed to mention.
Special thanks for the help for RB24 to YUV422 conversion:
Aron Rosenberg <[EMAIL PROTECTED]>
Thank the guys, who wrote the cpia driver (P.Prengler,S.Bertin,J.Erdfelt)
Lastly sourceforge for hosting the site
Check http://qce-ga.sourceforge.net for updates/news/mailing lists
Copyrights Major details at the beginning of quickcam.c, additional
copyrighted material yuv.c(JF.Clere) and testquickcam.c(N.Zimmermann) see
the beginnings of those files for detail.
Logitech Quickcam Express USB Driver
------------------------------------
1. How to compile?
Just use a plain "make" to compile the driver.
2a. The easiest way to load modules is to use the quickcam.sh shell script
To run it type ./quickcam.sh (If you have compiled USB and/or V4L support into
Kernel rather than as modules you should use method 2b)
2b. How to use with insmod?
Typing "insmod mod_quickcam.o" should do the trick.
3. What are qce-ga's features?
* 2.2 + 2.4 Support
* Video4Linux compatible driver
* ProcFS Support (+ VideoProcFS)
* RGB24 and YUV* support
* full mmap() and read() support
* ....
4. What are the parameters wich are accepted by mod_quickcam.o?
Easily find it out with "modinfo -p mod_quickcam.o"
insmod mod_quickcam.o debug=n (n=1,2,4,8,16,32 or any ored values).
insmod mod_quickcam.o rgain=red bgain=blue ggain=green.
red, blue and green are initial gain values this allows to correct the
colour of the images. The default values are rgain=192 bgain=192 ggain=176
Cheers,
Nikolas Zimmermann <[EMAIL PROTECTED]>
/*
* qce-ga, linux V4L driver for the Quickcam Express and Dexxa Quickcam
*
* Copyright (C) 2001 Jean-Fredric Clere,Nikolas Zimmermann, Georg Acher
* Mark Cave-Ayland, Carlo E Prelz
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
/* Cam variations of Logitech Quickcam Express:
P/N 861037: Sensor HDCS1000 ASIC STV0600
P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600
P/N 861055: Sensor ST VV6410 ASIC STV0610 ("LEGO cam" not _yet_ supported)
P/N 861075-0040: Sensor HDCS1000 ASIC
For any questions ask [EMAIL PROTECTED]!
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/proc_fs.h>
#include <linux/smp_lock.h>
#include <linux/videodev.h>
#include <linux/vmalloc.h>
#include <linux/wrapper.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/usb.h>
#include <asm/io.h>
#include "quickcam.h"
#include "pb0100.h"
#include "hdcs.h"
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#ifndef MODULE
#define DEBUGLEVEL 0 // put in your wanted value here
#endif
#ifdef MODULE
#define DEBUGLEVEL 0
MODULE_PARM(debug, "i");
MODULE_PARM(mode, "i");
MODULE_PARM(rgain, "i");
MODULE_PARM(bgain, "i");
MODULE_PARM(ggain, "i");
MODULE_PARM(video_nr,"i");
MODULE_PARM_DESC(debug, "Sets the debug output (1,2,4,8,16,32)");
MODULE_PARM_DESC(mode, "Sets the speed (0-1)");
MODULE_PARM_DESC(rgain, "Initial value of red gain (0-255)");
MODULE_PARM_DESC(bgain, "Initial value of blue gain (0-255)");
MODULE_PARM_DESC(ggain, "Initial value of green gains (0-255)");
MODULE_PARM_DESC(video_nr, "Set videodevice number (/dev/videoX)");
MODULE_SUPPORTED_DEVICE("video");
MODULE_DESCRIPTION("Logitech Quickcam Express Webcam driver");
MODULE_AUTHOR("see README");
MODULE_LICENSE("GPL");
#endif
static int debug = DEBUGLEVEL;
static int mode = 0; /* normal or sub-sample (sub-sample to increase the speed) */
/* gains allow the user decide the initial value of the gains */
static int rgain = 0;
static int bgain = 0;
static int ggain = 0;
/* video_nr option allows to specify a certain /dev/videoX device */
/* (like /dev/video0 or /dev/video1 ...) */
/* for autodetect first available use video_nr=-1 (defaultvalue) */
/* (code reused from bttv driver http://bytesex.org/bttv/) */
static int video_nr = -1;
/* Video Size 352 x 288 x 3 bytes for RGB */
#define MAX_FRAME_SIZE (352 * 288 * 3)
#define VERSION "$Id: quickcam.c,v 1.80 2001/06/16 15:25:09 wildfox Exp $"
static struct usb_driver quickcam_driver;
#if LINUX_VERSION_CODE >= 0x020400
static __devinitdata struct usb_device_id device_table [] = {
{ USB_DEVICE(0x046d, 0x0870) },
{ }
};
MODULE_DEVICE_TABLE (usb, device_table);
#define MAP_NR virt_to_page
#endif
/*******************************/
/* Memory management functions */
/*******************************/
/* Given PGD from the address space's page table, return the kernel
* virtual mapping of the physical memory mapped at ADR.
*/
static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr)
{
unsigned long ret = 0UL;
pmd_t *pmd;
pte_t *ptep, pte;
if (!pgd_none(*pgd)) {
pmd = pmd_offset(pgd, adr);
if (!pmd_none(*pmd)) {
ptep = pte_offset(pmd, adr);
pte = *ptep;
if (pte_present(pte)) {
ret = (unsigned long) page_address(pte_page(pte));
ret |= (adr & (PAGE_SIZE-1));
}
}
}
return ret;
}
static inline unsigned long uvirt_to_bus(unsigned long adr)
{
unsigned long kva, ret;
kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr);
ret = virt_to_bus((void *)kva);
return ret;
}
static inline unsigned long kvirt_to_bus(unsigned long adr)
{
unsigned long va, kva, ret;
va = VMALLOC_VMADDR(adr);
kva = uvirt_to_kva(pgd_offset_k(va), va);
ret = virt_to_bus((void *)kva);
return ret;
}
/* Here we want the physical address of the memory.
* This is used when initializing the contents of the
* area and marking the pages as reserved.
*/
static inline unsigned long kvirt_to_pa(unsigned long adr)
{
unsigned long va, kva, ret;
va = VMALLOC_VMADDR(adr);
kva = uvirt_to_kva(pgd_offset_k(va), va);
ret = __pa(kva);
return ret;
}
static void *rvmalloc(unsigned long size)
{
void *mem;
unsigned long adr, page;
/* Round it off to PAGE_SIZE */
size += (PAGE_SIZE - 1);
size &= ~(PAGE_SIZE - 1);
mem = vmalloc(size);
if (!mem)
return NULL;
memset(mem, 0, size); /* Clear the ram out, no junk to the user */
adr = (unsigned long) mem;
while (size > 0) {
page = kvirt_to_pa(adr);
mem_map_reserve(MAP_NR(__va(page)));
adr += PAGE_SIZE;
if (size > PAGE_SIZE)
size -= PAGE_SIZE;
else
size = 0;
}
return mem;
}
static void rvfree(void *mem, unsigned long size)
{
unsigned long adr, page;
if (!mem)
return;
size += (PAGE_SIZE - 1);
size &= ~(PAGE_SIZE - 1);
adr=(unsigned long) mem;
while (size > 0) {
page = kvirt_to_pa(adr);
mem_map_unreserve(MAP_NR(__va(page)));
adr += PAGE_SIZE;
if (size > PAGE_SIZE)
size -= PAGE_SIZE;
else
size = 0;
}
vfree(mem);
}
/*
* HexDump a string...
*/
static void usbvideo_HexDump(const unsigned char *data, int len)
{
const int bytes_per_line = 32;
char tmp[128]; /* 32*3 + 5 */
int i, k;
for (i=k=0; len > 0; i++, len--) {
if (i > 0 && ((i % bytes_per_line) == 0)) {
printk("%s\n", tmp);
k=0;
}
if ((i % bytes_per_line) == 0)
k += sprintf(&tmp[k], "[%04x]: ", i);
k += sprintf(&tmp[k], "%02x ", data[i]);
}
if (k > 0)
printk("%s\n", tmp);
}
/*
* /proc interface for our driver
*/
#if LINUX_VERSION_CODE >= 0x020400
#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
static struct proc_dir_entry *quickcam_proc_entry = NULL;
extern struct proc_dir_entry *video_proc_entry;
#define CHECK(x) ((x) ? "Yes" : "No")
#define CHOOSE(x,y,a,b) ((x == y) ? a : b)
static int quickcam_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
char *out = page;
int len;
struct usb_quickcam *dev = data;
out += sprintf(out, "*** Driver Status ***\n");
out += sprintf(out, "Driver Version : %s\n", VERSION);
out += sprintf(out, "Sensor : %s\n", CHOOSE(HDCS_ADDR, dev->sensor_addr, "HDCS", "Photobit"));
out += sprintf(out, "Streaming : %s\n", CHECK(dev->streaming));
out += sprintf(out, "Grabbing : %s\n", CHECK(dev->grabbing));
out += sprintf(out, "Reading frame : %d\n", dev->readframe);
out += sprintf(out, "*** Sensor Control ***\n");
out += sprintf(out, "Shutter value : %d\n", dev->shutter_val);
out += sprintf(out, "Gain : %d\n", dev->gain);
out += sprintf(out, "*** Output Window ***\n");
out += sprintf(out, "Width : %d\n", dev->vwin.width);
out += sprintf(out, "Height : %d\n", dev->vwin.height);
out += sprintf(out, "*** Output Picture ***\n");
out += sprintf(out, "Brightness : %d\n", dev->vpic.brightness);
out += sprintf(out, "Whiteness : %d\n", dev->vpic.whiteness);
out += sprintf(out, "Contrast : %d\n", dev->vpic.contrast);
out += sprintf(out, "Hue : %d\n", dev->vpic.hue);
out += sprintf(out, "Color : %d\n", dev->vpic.colour);
out += sprintf(out, "Palette : %s\n", FormatName(dev->vpic.palette));
len = out - page;
len -= off;
if(len < count)
{
*eof = 1;
if(len <= 0)
return 0;
}
else
len = count;
*start = page + off;
return len;
}
static int quickcam_write_proc(struct file *file, const char *buffer,
unsigned long count, void *data)
{
// we don't support this....yet?
return -EINVAL;
}
static void create_proc_quickcam(struct usb_quickcam *dev)
{
char name[7];
struct proc_dir_entry *entry;
if(!quickcam_proc_entry || !dev)
return;
sprintf(name, "video%d", dev->vdev.minor);
entry = create_proc_entry(name, S_IFREG | S_IRUGO | S_IWUSR, quickcam_proc_entry);
if(!entry)
return;
entry->data = dev;
entry->read_proc = quickcam_read_proc;
entry->write_proc = quickcam_write_proc;
dev->proc_entry = entry;
}
static void destroy_proc_quickcam(struct usb_quickcam *dev)
{
char name[7];
if(!dev->proc_entry || !dev)
return;
sprintf(name, "video%d", dev->vdev.minor);
remove_proc_entry(name, quickcam_proc_entry);
dev->proc_entry = NULL;
}
static void proc_quickcam_create(void)
{
if(!video_proc_entry)
return;
quickcam_proc_entry = create_proc_entry("quickcam", S_IFDIR, video_proc_entry);
if(quickcam_proc_entry)
quickcam_proc_entry->owner = THIS_MODULE;
}
static void proc_quickcam_destroy(void)
{
if(!quickcam_proc_entry)
return;
remove_proc_entry("quickcam", video_proc_entry);
}
#endif
#endif
/*
* I2C read registers of HDCS1000, the result will be in the STV0600 register 0x1410.
*/
static int usb_quickcam_i2c_in(struct usb_device *dev, int reg,
unsigned char sensor_addr)
{
char buff[35]; /* why 35 = 23 hex? */
buff[0]=reg;
buff[0x20]=sensor_addr;
buff[0x21]=0; // 1 value
buff[0x22]=3; // Read cmd.
return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
0x04,
0x40,
0x400, 0,
buff, 0x23 , HZ);
}
/* read one byte identification register for HDCS.
* write command to sensor.
* read the STV0600.
*/
static int usb_quickcam_get_i2c(struct usb_device *dev, void *buf)
{
if (usb_quickcam_i2c_in(dev,HDCS_IDENT + 1,HDCS_ADDR)<0) {
printk("usb_quickcam_i2c_in failed\n");
return(-1);
}
return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
0x04,
0xC0,
0x1410, 0, buf, 1, HZ);
}
/* read two byte identification register for PB */
static int usb_quickcam_get_i2c2(struct usb_device *dev, void *buf)
{
if (usb_quickcam_i2c_in(dev,PB_IDENT,PB_ADDR)<0) {
printk("usb_quickcam_i2c_in failed\n");
return(-1);
}
return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
0x04,
0xC0,
0x1410, 0, buf, 2, HZ);
}
/*
* Set register one byte
*/
int usb_quickcam_set1(struct usb_device *dev, short reg, char val)
{
char buff[1];
buff[0] = val;
return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
0x04,
0x40,
reg, 0,
buff, 1, HZ);
}
/*
* Set register two byte
*/
int usb_quickcam_set2(struct usb_device *dev, short reg, short val)
{
char buff[2];
buff[0] = val&0xFF;
buff[1] = (val>>8)&255;
return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
0x04,
0x40,
reg, 0,
buff, 2, HZ);
}
/*
* I2C set registers of HDCS100 (8 bits registers)
* Why not grouped in big blocs?
*/
int usb_quickcam_i2c_out(struct usb_device *dev, int reg, int val)
{
char buff[35]; /* why 35 = 23 hex? */
buff[0]=reg;
buff[0x10]=val;
buff[0x20]=HDCS_ADDR;
buff[0x21]=0; // 1 value
buff[0x22]=1; // Write cmd, 03 would be read.
return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
0x04,
0x40,
0x400, 0,
buff, 0x23 , HZ);
}
/* Send a command to the sensor */
int quickcam_usb_control_msg(struct usb_device *dev, char *buff)
{
return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
0x04,
0x40,
0x400, 0,
buff, 0x23 , HZ);
}
/*
* I2C set registers of PB100 (16 bits registers).
*/
int usb_quickcam_i2c_out2(struct usb_device *dev, int reg, int val)
{
char buff[35]; /* why 35 = 23 hex? */
buff[0]=reg;
buff[0x10]=val&255;
buff[0x11]=(val>>8)&255;
buff[0x20]=PB_ADDR;
buff[0x21]=0; // 1 value
buff[0x22]=1; // Write cmd, 03 would be read.
return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
0x04,
0x40,
0x400, 0,
buff, 0x23 , HZ);
}
/*
* Set the exposure: note the val is a long, kernel does not know __divdi3,
* it is in libc but it a big piece of code...
* Of this is a copy ;=) of Georg Acher ([EMAIL PROTECTED]) set_exposure().
* And he says: "Auto-Exposure needs a bit more intelligence"!
*/
static int usb_quickcam_set_exposure(struct usb_quickcam *quickcam,
int val)
{
int og=quickcam->gain;
int os=quickcam->shutter_val;
struct usb_device *dev = quickcam->dev;
if (debug&DEBUGDATA)
printk("usb_quickcam_set_exposure: %d gain %d shutter %d\n",val,quickcam->gain,quickcam->shutter_val);
if (val>140)
quickcam->gain-=4+(val-140)/4;
if (val<90)
quickcam->gain+=4+(90-val)/4;
if (quickcam->gain<=40)
{
if (quickcam->shutter_val>16)
{
quickcam->gain+=16;
quickcam->gain = min(255,quickcam->gain);
quickcam->shutter_val-=16;
if (os!=quickcam->shutter_val)
quickcam->sensor_ctrl.set_shutter(dev,quickcam->shutter_val,0x100);
}
else
if (quickcam->gain<2)
quickcam->gain=2;
}
if ((quickcam->gain+abs(quickcam->vpic.whiteness / 256))>255 && (quickcam->shutter_val<500))
{
quickcam->gain-=16;
quickcam->gain = max(0,quickcam->gain);
quickcam->shutter_val+=16;
if (os!=quickcam->shutter_val)
quickcam->sensor_ctrl.set_shutter(dev,quickcam->shutter_val,0x100);
}
if (og!=quickcam->gain)
{
return quickcam->sensor_ctrl.set_gains(dev, quickcam->gain,quickcam->vpic.whiteness / 256);
}
return 0;
}
/* Stop camera and disable ISO-streaming */
static int usb_quickcam_stop(struct usb_quickcam *quickcam)
{
int ret=0;
if (!quickcam->dev)
return 0; // nothing to do.
// stop ISO-streaming.
if (usb_quickcam_set1(quickcam->dev, STV_ISO_ENABLE, 0)<0)
ret = -1;
// stop current frame.
ret = quickcam->sensor_ctrl.stop(quickcam->dev);
if (debug&DEBUGLOGIC)
printk("usb_quickcam_stop:%d\n",ret);
return ret;
}
/*
* Change the control register to start an image capture.
*/
static int quickcam_init_isoc(struct usb_quickcam *quickcam);
static int quickcam_stop_isoc(struct usb_quickcam *quickcam);
static int usb_quickcam_upload_frame(struct usb_quickcam *quickcam)
{
if (debug&DEBUGLOGIC)
printk("usb_quickcam_upload_frame %d\n",quickcam->scanstate);
if (quickcam->scanstate==STATE_ERROR) {
quickcam->grabbing = 0;
// stop the hardware and reinitialise the USB.
if (quickcam_stop_isoc(quickcam)<0)
return -1;
if (quickcam_init_isoc(quickcam)<0)
return -1;
quickcam->scanstate = STATE_OK; // otherwise error forever!
}
/* if we are not grabbing start grabbing */
if (!quickcam->grabbing) {
if (debug&DEBUGLOGIC)
printk("usb_quickcam_upload_frame start grabbing\n");
if (quickcam->sensor_ctrl.start(quickcam->dev)<0)
{
if (debug&DEBUGLOGIC)
printk("usb_quickcam_upload_frame ...grabbing NOT started\n");
return -1;
}
if (usb_quickcam_set1(quickcam->dev, STV_ISO_ENABLE, 1)<0)
{
if (debug&DEBUGLOGIC)
printk("usb_quickcam_upload_frame ...grabbing NOT started (iso enabling failed :()\n");
return -1;
}
quickcam->grabbing = -1;
}
return 0;
}
/*
* Streamformat, as delivered by the camera:
*
* Raw image data for Bayer-RGB-matrix:
* G R for even rows
* B G for odd rows
* Well not when frame by frame. It starts with 80 01 00 00 for the first time,
* that 02 00 HH LL (and there is some grabages after the frame,
* from time to time a 80 02 00 00 80 01 00 00 is received, it seems it is a
* start of frame (at least it is handled as if it were one.
*
* Convert the Bayer-RGB to RBG24.
*
* Note:
*
* A trick is used to calculate medium value:
* a long long cannot be used in 32 bits processors (like 386 family)
* so I calculate a medium value per line, and sum the line values.
*
*/
static void quickcam_parse_data(struct usb_quickcam *quickcam, int curframe)
{
#define addblue 0
#define addred 2
/* need cleaning ... */
#define XH quickcam->vwin.height
#define XW quickcam->vwin.width
struct quickcam_frame *frame;
int i;
unsigned long mid_value=0;
unsigned long mid_value_line=0;
int xx=0;
int yy=0;
int xd=0;
int copylen=0;
unsigned char *o;
unsigned char *data;
frame = &quickcam->frame[curframe];
if (debug&DEBUGDATA)
printk("quickcam_parse_data %ld for frame %d\n",
frame->storelength,curframe);
/* Convert the current frame */
data = frame->storedata;
for (i=0;i<frame->storelength;i++) {
/*
* For HDCS Sensors:
* Camera is G1R1 G2R2 G3R3...
* B4G4 B5G5 B6G6...
* Video is B4G1R1 B4G1R1
* B4G4R1 B4G4R1
* For Photobit...
*/
if (yy>XH)
yy=XH;
if (mode) {
o=((char*)frame->data)+3*xd+(XW*3*yy);
xd++;
xd++;
} else
o=((char*)frame->data)+3*xx+(XW*3*yy);
mid_value_line+=*data;
if (!(yy&1)) { // even row
if (xx&1) { // odd column, red
*(o+addred)=*data;
*(o+addred-3)=*data;
*(o+addred+3*(XW))=*data;
*(o+addred+3*(XW-1))=*data;
if (mode) {
*(o+addred+3)=*data;
*(o+addred-6)=*data;
*(o+addred+3*(XW+1))=*data;
*(o+addred+3*(XW-2))=*data;
}
} else { // green
*(o+1)=*data;
*(o+1+3)=*data;
if (mode) {
*(o+1+6)=*data;
if (xd>2)
*(o+1-3)=*data;
}
}
} else {
if (xx&1) { // odd column green
*(o+1)=*data;
*(o+1-3)=*data;
if (mode) {
*(o+1+3)=*data;
*(o+1-6)=*data;
*(o+1+6)=*data; // last would be missing.
}
} else { //blue
*(o+addblue)=*data;
*(o+addblue+3)=*data;
*(o+addblue+3*(XW))=*data;
*(o+addblue+3*(XW-1))=*data;
if (mode) {
*(o+addblue+6)=*data;
*(o+addblue+3*(XW+1))=*data;
*(o+addblue-6)=*data;
*(o+addblue+3*(XW-2))=*data;
}
}
}
xx++;
copylen ++;
if (xx==XW || (xx==XW/2 && mode)) {
if (mode)
mid_value += (mid_value_line/(XW/2));
else
mid_value += (mid_value_line/XW);
mid_value_line = 0;
xx=0;
xd = 0;
yy++;
if (yy>=XH) {
if (debug&DEBUGDATA)
printk("skipped %ld bytes\n",frame->storelength-i);
break;
}
}
data++;
}
/*
* the frame is done, the exposure should
* be set here (We are the waked-up process)
*/
if (yy>=XH) {
mid_value = mid_value/yy;
usb_quickcam_set_exposure(quickcam,mid_value);
}
frame->scanlength = (copylen*6)/2; // yes but how to compute partial pixels!.
}
/*
* Analyse the data and store it
* This routine has been written by Carlo E. Prelz, [EMAIL PROTECTED]
* Image starts with 80 01 00 00
* and ends with 80 02 00 00
* The actual unknown messages are:
* 80 05 00 00 (Assume it is start)
* 80 06 00 00 (Assume it is end)
* C0 01 00 00 (Assume it is start).
* C0 02 00 00 (Assume it is end).
* C0 06 00 00 (? like 80 06 00 00).
* C0 05 00 00 (Not found in my tries, but I have the feeling it exists).
* Chunk-Header 02 00 HH LL, followed by chunk with HHLL bytes
* or 42 00 HH LL.
*/
static void quickcam_parse_store(struct urb *urb)
{
unsigned char type1,type2,*ptr;
int i,cur_len,tot_len,left;
unsigned int frag_len=0;
struct quickcam_frame *pframe=NULL;
int framesize;
struct usb_quickcam *quickcam;
if (!urb)
return;
quickcam = urb->context;
if (!quickcam)
return;
if (!quickcam->dev)
return;
if (!quickcam->streaming) {
if (debug&DEBUGINT)
printk("quickcam: oops, not streaming, but interrupt\n");
return;
}
if (mode)
framesize = min(BAYER_FRAME_SIZE,(quickcam->vwin.height*quickcam->vwin.width)/2);
else
framesize = min(BAYER_FRAME_SIZE,quickcam->vwin.height*quickcam->vwin.width);
/* Check the length */
for(i=0,tot_len=quickcam->scratchlen;i<urb->number_of_packets;i++) {
if(!urb->iso_frame_desc[i].status)
tot_len+=urb->iso_frame_desc[i].actual_length;
else
printk("quickcam data error: [%d] len=%d, status=%X\n",
i,
urb->iso_frame_desc[i].actual_length,
urb->iso_frame_desc[i].status);
}
if(tot_len<=quickcam->scratchlen)
return;
if (tot_len>=SCRATCH_BUF_SIZE) {
printk("quickcam_parse_store: scratch overflow\n");
return;
}
/* store the data (we skip the iso_frame in error) */
if (debug&DEBUGINT)
printk("quickcam_parse_store: %d\n",tot_len);
ptr=quickcam->scratch+quickcam->scratchlen;
for(i=0;i<urb->number_of_packets;i++) {
if(!urb->iso_frame_desc[i].status &&
urb->iso_frame_desc[i].actual_length) {
memcpy(ptr,urb->transfer_buffer+urb->iso_frame_desc[i].offset,
urb->iso_frame_desc[i].actual_length);
ptr+=urb->iso_frame_desc[i].actual_length;
// usbvideo_HexDump(urb->transfer_buffer+urb->iso_frame_desc[i].offset,8);
}
}
/* process the data */
pframe = NULL;
if (quickcam->curframe!=-1) {
if (quickcam->storestate==FRAME_STORE) {
pframe = &quickcam->frame[quickcam->curframe];
}
}
if (debug&DEBUGINT)
printk("quickcam_parse_store process data: %d curframe: %d\n",tot_len,quickcam->curframe);
for(cur_len=0;cur_len+4<=tot_len;) {
type1=quickcam->scratch[cur_len];
type2=quickcam->scratch[cur_len+1];
frag_len=quickcam->scratch[cur_len+2]<<8 | quickcam->scratch[cur_len+3];
if (debug&DEBUGINT)
printk("quickcam_parse_store %d %02x %02x\n",frag_len,0xFF&type1, 0xFF&type2);
if(type1==0x80 || type1==0xC0) {
if(type2==1 || type2==5) { // a frame begins - see if we can capture it.
if (pframe) {
if (pframe->grabstate != FRAME_READY) {
printk("quickcam: frame %d not free. Skipping...!\n",
quickcam->curframe);
pframe=NULL;
}
} else {
// Check if we were waiting for a start of frame.
if (quickcam->curframe!=-1) {
pframe = &quickcam->frame[quickcam->curframe];
quickcam->storestate=FRAME_STORE;
}
}
cur_len+=4;
continue;
} else if(type2==2 || type2==6) { // a frame end
if(pframe) {
if(pframe->storelength!=framesize && quickcam->sizechanged) {
/*
* That is possible that the size has been changed
* during the frame capture in this case the next frames will have
* the correct size.
* The PB100 reacts a stange way it first changes the width:
* oldwith * oldheight.
* newwith * oldheight.
* newwith * newheight. (Strange isn't it?).
*/
printk("quickcam: frame size is the old one? (%ld)\n",
pframe->storelength);
cur_len+=4;
pframe->storelength = 0;
pframe->grabstate = FRAME_READY;
continue;
}
pframe->grabstate=FRAME_DONE;
if(pframe->storelength!=framesize)
printk("quickcam: frame size is incorrect! (%ld)\n",
pframe->storelength);
else
quickcam->sizechanged = 0;
if(waitqueue_active(&pframe->wq))
wake_up_interruptible(&pframe->wq);
/* check if next one could be processed. */
if (quickcam->frame[(quickcam->curframe + 1) % 2].grabstate == FRAME_READY) {
quickcam->curframe = (quickcam->curframe + 1) % 2;
if (debug&DEBUGINT)
printk("quickcam: marking as success next: %d\n",
quickcam->curframe);
pframe = &quickcam->frame[quickcam->curframe];
} else {
pframe = NULL;
if (debug&DEBUGINT)
printk("quickcam: marking as success stop\n");
quickcam->curframe = -1;
}
}
cur_len+=4;
continue;
}
} else if(type1==0x02 || type1==0x42) { /* data */
if (4+frag_len+cur_len>tot_len) break; // not enough data.
if (debug&DEBUGINT)
printk("quickcam: storing %d already in %ld\n",frag_len,pframe->storelength);
if(pframe) {
if (pframe->grabstate==FRAME_READY) {
pframe->grabstate = FRAME_GRABBING;
pframe->storelength = 0;
}
if(pframe->storelength+frag_len>framesize) {
if (pframe->storelength!=framesize)
printk("quickcam: warning -> frame overflow (%ld)\n",
pframe->storelength+frag_len-framesize);
if (framesize>pframe->storelength) {
// The framesize may be smaller as what is already store!.
memcpy(pframe->storedata+pframe->storelength,
quickcam->scratch+cur_len+4,
framesize - pframe->storelength);
}
pframe->storelength=framesize;
} else {
memcpy(pframe->storedata+pframe->storelength,
quickcam->scratch+cur_len+4,frag_len);
pframe->storelength+=frag_len;
}
} else {
quickcam->storestate=FRAME_SKIP; // skip until next frame.
}
cur_len+=4+frag_len;
continue;
}
/*
* must be garbage, and probably a big disaster...
* Trying to find a start (VSYNC) as done in the old parse_data() could crash the box,
* because we are in a interruption routine and we are blocking the box.
* so we just dump the first 20 bytes (hopefully we find what to do with it one day,
* as it had happened with the other sequences.).
*
*/
printk("quickcam got garbage at %ld\n",pframe->storelength);
left = tot_len - cur_len;
left = min(20,left);
usbvideo_HexDump(quickcam->scratch+cur_len,left);
if(pframe) {
pframe->grabstate=FRAME_ERROR;
if(waitqueue_active(&pframe->wq))
wake_up_interruptible(&pframe->wq);
}
quickcam->scratchlen = 0;
return;
}
/* save remeaning data, probably there nothing, as Carlo was just ignoring this case. */
left = tot_len - cur_len;
if (left>0) {
if (debug&DEBUGINT)
printk("quickcam: save remeaning data: %d\n",left);
memmove(quickcam->scratch, quickcam->scratch+cur_len, left);
quickcam->scratchlen = left;
} else
quickcam->scratchlen = 0;
}
/*
* Start isoc
*/
static int quickcam_init_isoc(struct usb_quickcam *quickcam)
{
urb_t *urb;
int fx, err;
if (debug&DEBUGLOGIC)
printk("quickcam_init_isoc\n");
/* Alternate interface 3 is is the biggest frame size */
/* JFC use 1: but do not know why */
if (usb_set_interface(quickcam->dev, quickcam->iface, 1) < 0) {
printk("usb_set_interface error\n");
return -EBUSY;
}
/* We double buffer the Iso lists */
urb = usb_alloc_urb(FRAMES_PER_DESC);
if (!urb) {
printk("quickcam_init_isoc: usb_init_isoc ret %d\n",
0);
return -ENOMEM;
}
/* first buffer */
quickcam->sbuf[0].urb = urb;
urb->dev = quickcam->dev;
urb->context = quickcam;
urb->pipe = usb_rcvisocpipe(quickcam->dev, _QUICKCAM_ISOPIPE);
//printk("quickcam_init_isoc pipesize %d\n", usb_maxpacket (quickcam->dev, urb->pipe, usb_pipeout (urb->pipe)));
urb->transfer_flags = USB_ISO_ASAP;
urb->transfer_buffer = quickcam->sbuf[0].data;
urb->complete = quickcam_parse_store;
urb->number_of_packets = FRAMES_PER_DESC;
urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC;
for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx;
urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC;
}
urb = usb_alloc_urb(FRAMES_PER_DESC);
if (!urb) {
printk("quickcam_init_isoc: usb_init_isoc ret %d\n",
0);
return -ENOMEM;
}
/* second buffer */
quickcam->sbuf[1].urb = urb;
urb->dev = quickcam->dev;
urb->context = quickcam;
urb->pipe = usb_rcvisocpipe(quickcam->dev, _QUICKCAM_ISOPIPE);
urb->transfer_flags = USB_ISO_ASAP;
urb->transfer_buffer = quickcam->sbuf[1].data;
urb->complete = quickcam_parse_store;
urb->number_of_packets = FRAMES_PER_DESC;
urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC;
for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx;
urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC;
}
quickcam->sbuf[1].urb->next = quickcam->sbuf[0].urb;
quickcam->sbuf[0].urb->next = quickcam->sbuf[1].urb;
err = usb_submit_urb(quickcam->sbuf[0].urb);
if (err)
printk("quickcam_init_isoc: usb_submit_urb(0) ret %d\n",
err);
err = usb_submit_urb(quickcam->sbuf[1].urb);
if (err)
printk("quickcam_init_isoc: usb_submit_urb(1) ret %d\n",
err);
quickcam->streaming = 1;
if (debug&DEBUGLOGIC)
printk("quickcam_init_isoc finished\n");
return 0;
}
/*
* Set size of window sensor, note that it is not yet center!
* The mode is used to allow higher scan rate with smaller images.
*/
static int quickcam_set_size(struct usb_quickcam *quickcam, int width, int height)
{
if (height > 288 || height<0)
return -1;
if (width > 352 || width<0)
return -1;
if (quickcam->sensor_ctrl.set_size(quickcam->dev, mode)<0) {
printk("set_size sensor failed\n");
return -1;
}
/* set the requested size (we should try to center it) */
if (quickcam->sensor_ctrl.set_window(quickcam->dev, 0, 0,width,height)<0) {
printk("set_window sensor failed\n");
return(-1);
}
quickcam->vwin.width = width;
quickcam->vwin.height = height;
quickcam->sizechanged = -1; // changed!
return(0);
}
/*
* Initialise sensor and set shutter
* The Photobit starts the pixel integration inmendiatly after
* the reset... That why I moved this out usb_quickcam_configure().
*/
int quickcam_init_sensor(struct usb_quickcam *quickcam)
{
if (debug&DEBUGBASE)
printk("quickcam_init_sensor: init sensor\n");
if (quickcam->sensor_ctrl.init(quickcam->dev,mode, rgain, bgain, ggain)<0) {
printk("Init sensor failed\n");
return(-1);
}
if (quickcam->sensor_ctrl.set_gains(quickcam->dev,quickcam->gain,quickcam->vpic.whiteness / 256)<0) {
printk("set_gains sensor failed\n");
return(-1);
}
if (quickcam->sensor_ctrl.set_shutter(quickcam->dev,quickcam->shutter_val,0x100)<0) {
printk("set_shutter sensor failed\n");
return(-1);
}
/* Set the size otherwise the read() will fail */
/*
* JFC have to arrange this .... and use the value from the quickcam structure!
* NO: It is called in open() ... For the moment...
*/
if (quickcam_set_size(quickcam,352,288)<0)
return(-1);
return 0;
}
static int quickcam_stop_isoc(struct usb_quickcam *quickcam)
{
if (!quickcam->dev)
return -1;
if (debug&DEBUGLOGIC)
printk("quickcam_stop_isoc\n");
/* Turn off continuous grab */
if (usb_quickcam_stop(quickcam) < 0) {
printk("usb_quickcam_stop error\n");
return -EBUSY;
}
/* Set packet size to 0 */
if (usb_set_interface(quickcam->dev, quickcam->iface, 0) < 0) {
printk("usb_set_interface error\n");
return -EINVAL;
}
quickcam->streaming = 0;
/* make sure that the next is called */
if (quickcam->sbuf[1].urb)
quickcam->sbuf[1].urb->next = NULL;
if (quickcam->sbuf[0].urb)
quickcam->sbuf[0].urb->next = NULL;
/* Unschedule all of the iso td's */
if (quickcam->sbuf[1].urb) {
usb_unlink_urb(quickcam->sbuf[1].urb);
usb_free_urb(quickcam->sbuf[1].urb);
quickcam->sbuf[1].urb = NULL;
}
if (quickcam->sbuf[0].urb) {
usb_unlink_urb(quickcam->sbuf[0].urb);
usb_free_urb(quickcam->sbuf[0].urb);
quickcam->sbuf[0].urb = NULL;
}
return 0;
}
/*
* Start grabing a new frame it must be the one required otherwise the calling
* process will be lost (it waits for the wrong frame!).
*/
static int quickcam_new_frame(struct usb_quickcam *quickcam, int framenum)
{
struct quickcam_frame *frame;
int width, height;
if (debug&DEBUGLOGIC)
printk("quickcam_new_frame %d\n",framenum);
if (!quickcam->dev)
return -1;
if (quickcam->grabbing) {
quickcam->frame[framenum].grabstate = FRAME_READY;
quickcam->frame[framenum].scanlength = 0;
if (quickcam->curframe == -1)
quickcam->curframe = framenum;
return 0;
}
/* If we're not grabbing a frame right now and the other frame is */
/* ready to be grabbed into, then use it instead */
if (quickcam->curframe == -1) {
if (quickcam->frame[(framenum + 1) % 2].grabstate == FRAME_READY)
framenum = (framenum + 1) % 2;
} else {
/* previous frame errored, make sure we restart using the other one */
if (quickcam->scanstate==STATE_ERROR) {
//printk("grabing but STATE_ERROR (frame:%d)\n",quickcam->curframe);
/* restart the grabing in the other frame */
framenum = (quickcam->curframe -1 + QUICKCAM_NUMFRAMES) % QUICKCAM_NUMFRAMES;
} else {
if (debug&DEBUGLOGIC)
printk("quickcam_new_frame %d ending\n",quickcam->curframe);
return 0;
}
}
frame = &quickcam->frame[framenum];
width = frame->width;
height = frame->height;
/* Make sure it's not too big */
if (width > 352)
width = 352;
width = (width / 8) * 8; /* Multiple of 8 */
if (height > 288)
height = 288;
height = (height / 4) * 4; /* Multiple of 4 */
if (width<8) width=8;
if (height<4) height=4;
/* Set the ROI they want, xawtv tries a small piece first time. */
if (quickcam->vwin.height!=height && quickcam->vwin.width!=width) {
if (quickcam_set_size(quickcam, width, height)<0) {
if(debug&DEBUGLOGIC)
printk("quickcam_new_frame roi failed... EBUSY\n");
return -EBUSY;
}
}
/* set all the parameters for the irq modules */
frame->grabstate = FRAME_READY;
frame->scanlength = 0; /* accumulated in quickcam_parse_data() */
quickcam->curframe = framenum;
/* Grab the frame (will cause the irq's) */
if (usb_quickcam_upload_frame(quickcam) < 0) {
if(debug&DEBUGLOGIC)
printk("quickcam_upload_frame error\n");
quickcam->scanstate = STATE_ERROR;
frame->grabstate = FRAME_UNUSED;
quickcam->curframe = -1;
quickcam->grabbing = 0; // not grabbing.
return -EBUSY;
}
if (debug&DEBUGLOGIC)
printk("quickcam_new_frame %d end success\n",framenum);
return 0;
}
static int quickcam_init_done(struct video_device *dev)
{
#if LINUX_VERSION_CODE >= 0x020400
#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
create_proc_quickcam((struct usb_quickcam *) dev);
#endif
#endif
return 0;
}
static int quickcam_get_depth(struct usb_quickcam *quickcam)
{
switch(quickcam->vpic.palette)
{
case VIDEO_PALETTE_RGB24:
return 24;
case VIDEO_PALETTE_YUV420:
return 24;
case VIDEO_PALETTE_YUV422:
return 16;
case VIDEO_PALETTE_YUYV:
return 16;
default:
return -EINVAL;
}
}
/* Video 4 Linux API */
static int quickcam_open(struct video_device *dev, int flags)
{
int err = -EBUSY;
struct usb_quickcam *quickcam = (struct usb_quickcam *)dev;
if (debug&DEBUGLOGIC)
printk("quickcam_open\n");
down(&quickcam->lock);
if (quickcam->user)
goto out_unlock;
// Re-Setup internal video_picture
quickcam->vpic.colour = 0x8000;
quickcam->vpic.hue = 0x8000;
quickcam->vpic.brightness = 180 * 256;
quickcam->vpic.contrast = 128 * 256;
quickcam->vpic.whiteness = 0;
quickcam->vpic.palette = VIDEO_PALETTE_RGB24;
quickcam->vpic.depth = quickcam_get_depth(quickcam);
// Re-Setup internal video_window
quickcam->vwin.x = 0;
quickcam->vwin.y = 0;
quickcam->vwin.chromakey = 0;
quickcam->vwin.flags = 30; /* 30 fps */
quickcam->frame[0].grabstate = FRAME_UNUSED;
quickcam->frame[1].grabstate = FRAME_UNUSED;
quickcam->streaming = 0;
quickcam->curframe = -1;
quickcam->scratchlen = 0;
quickcam->storestate=FRAME_STORE;
err = -ENOMEM;
/* Allocate memory for the frame buffers */
quickcam->fbuf = rvmalloc(2 * MAX_FRAME_SIZE);
if (!quickcam->fbuf)
goto out_unlock;
quickcam->frame[0].data = quickcam->fbuf;
quickcam->frame[1].data = quickcam->fbuf + MAX_FRAME_SIZE;
quickcam->sbuf[0].data = kmalloc (FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL);
if (!quickcam->sbuf[0].data)
goto open_err_on0;
quickcam->sbuf[1].data = kmalloc (FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL);
if (!quickcam->sbuf[1].data)
goto open_err_on1;
quickcam->frame[0].storedata = vmalloc (BAYER_FRAME_SIZE);
if (!quickcam->frame[0].storedata)
goto open_err_on2;
quickcam->frame[1].storedata = vmalloc (BAYER_FRAME_SIZE);
if (!quickcam->frame[1].storedata)
goto open_err_on3;
/*
* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used
* (when using read()).
*/
quickcam->frame[0].width = 352;
quickcam->frame[0].height = 288;
quickcam->frame[0].bytes_read = 0;
quickcam->frame[1].width = 352;
quickcam->frame[1].height = 288;
quickcam->frame[1].bytes_read = 0;
/* initialise the USB things */
err = quickcam_init_isoc(quickcam);
if (err)
goto open_err_init;
/* Start the sensor */
err = quickcam_init_sensor(quickcam);
if (err)
goto open_err_init;
quickcam->user++;
quickcam->grabbing=0; // we are not grabbing.
quickcam->curframe=-1; // no frame in progress.
quickcam->readframe = -1; // no frame beeing read
up(&quickcam->lock);
MOD_INC_USE_COUNT;
if (debug&DEBUGLOGIC)
printk("quickcam_open successfull\n");
return 0;
open_err_init:
vfree (quickcam->frame[1].storedata);
open_err_on3:
vfree (quickcam->frame[0].storedata);
open_err_on2:
kfree (quickcam->sbuf[1].data);
open_err_on1:
kfree (quickcam->sbuf[0].data);
open_err_on0:
rvfree(quickcam->fbuf, 2 * MAX_FRAME_SIZE);
out_unlock:
up(&quickcam->lock);
if (debug&DEBUGLOGIC)
printk("quickcam_open failed\n");
return err;
}
static void quickcam_close(struct video_device *dev)
{
struct usb_quickcam *quickcam = (struct usb_quickcam *)dev;
if (debug&DEBUGLOGIC)
printk("quickcam_close\n");
down(&quickcam->lock);
quickcam->user--;
MOD_DEC_USE_COUNT;
quickcam_stop_isoc(quickcam);
quickcam->grabbing = 0; // not grabbing.
quickcam->readframe = -1; // no frame beeing read
quickcam->sizechanged = 0; // frame unchanged!
rvfree(quickcam->fbuf, 2 * MAX_FRAME_SIZE);
kfree(quickcam->sbuf[1].data);
kfree(quickcam->sbuf[0].data);
vfree(quickcam->frame[1].storedata);
vfree(quickcam->frame[0].storedata);
up(&quickcam->lock);
if (!quickcam->dev) {
video_unregister_device(&quickcam->vdev);
kfree(quickcam);
}
}
static long quickcam_write(struct video_device *dev, const char *buf, unsigned long count, int noblock)
{
return -EINVAL;
}
static int quickcam_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
{
struct usb_quickcam *quickcam = (struct usb_quickcam *)dev;
int retval = 0;
if (!quickcam->dev)
return -EIO;
if (down_interruptible(&quickcam->busy_lock))
return -EINTR;
switch (cmd) {
case VIDIOCGCAP:
{
struct video_capability b;
if (debug&DEBUGIOCTL)
printk("quickcam_ioctl: VIDIOCGCAP\n");
strcpy(b.name, "Logitech USB Camera");
b.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE;
b.channels = 1;
b.audios = 0;
b.maxwidth = 352; /* CIF */
b.maxheight = 288; /* " */
b.minwidth = 176;
b.minheight = 144;
if (copy_to_user(arg, &b, sizeof(b)))
retval = -EFAULT;
break;
}
case VIDIOCGCHAN:
{
struct video_channel v;
if (debug&DEBUGIOCTL)
printk("quickcam_ioctl: VIDIOCGCHAN\n");
if (copy_from_user(&v, arg, sizeof(v))) {
retval = -EFAULT;
break;
}
if (v.channel != 0) {
retval = -EINVAL;
break;
}
v.flags = 0;
v.tuners = 0;
v.type = VIDEO_TYPE_CAMERA;
strcpy(v.name, "Camera");
if (copy_to_user(arg, &v, sizeof(v)))
retval = -EFAULT;
break;
}
case VIDIOCSCHAN:
{
int v;
if (copy_from_user(&v, arg, sizeof(v))) {
retval = -EFAULT;
break;
}
if (debug&DEBUGIOCTL)
printk("quickcam_ioctl: VIDIOCSCHAN %d\n",v);
if (v != 0)
retval = -EINVAL;
break;
}
case VIDIOCGPICT:
{
if (debug&DEBUGIOCTL)
printk("quickcam_ioctl: VIDIOCGPICT\n");
if (copy_to_user(arg, &quickcam->vpic, sizeof(quickcam->vpic)))
retval = -EFAULT;
break;
}
case VIDIOCSPICT:
{
struct video_picture p;
if (debug&DEBUGIOCTL)
printk("quickcam_ioctl: VIDIOCSPICT\n");
if (copy_from_user(&p, arg, sizeof(p)))
retval = -EFAULT;
else {
/* Black and white only?... */
if(p.whiteness >= 256 && !(p.whiteness >= 150 * 256)) {
if((p.whiteness / 256) != quickcam->vpic.whiteness / 256) {
quickcam->vpic.whiteness = p.whiteness;
quickcam->sensor_ctrl.set_gains(quickcam->dev, quickcam->gain, quickcam->vpic.whiteness / 256);
}
}
/* xawtv use this to find the supported format */
if (debug&DEBUGIOCTL)
printk("quickcam_ioctl: VIDIOCSPICT depth: %d palette %d\n",p.depth, p.palette);
if (p.depth!=24)
retval = -EINVAL;
else {
retval = IsSupported(p.palette);
if (!retval)
quickcam->vpic.palette = p.palette;
}
}
if (debug&DEBUGIOCTL)
printk("quickcam_ioctl: VIDIOCSPICT return %d\n",retval);
break;
}
case VIDIOCSWIN:
{
struct video_window vw;
if (debug&DEBUGIOCTL)
printk("quickcam_ioctl: VIDIOCSWIN\n");
if (copy_from_user(&vw, arg, sizeof(vw))) {
retval = -EFAULT;
break;
}
printk("quickcam_ioctl: VIDIOCSWIN vw.flags %d vw.clipcount %d\n",vw.flags,vw.clipcount);
/* set the size only if changed */
if (quickcam->vwin.width != vw.width || quickcam->vwin.height != vw.height) {
// Set requested size.
if (quickcam_set_size(quickcam,vw.width,vw.height)<0)
retval = -EINVAL;
}
if (debug&DEBUGIOCTL)
printk("quickcam_ioctl: VIDIOCSWIN returns %d\n",retval);
break;
}
case VIDIOCGWIN:
{
if (debug&DEBUGIOCTL)
printk("quickcam_ioctl: VIDIOCGWIN\n");
if (copy_to_user(arg, &quickcam->vwin, sizeof(quickcam->vwin)))
retval = -EFAULT;
break;
}
case VIDIOCGMBUF:
{
struct video_mbuf vm;
if (debug&DEBUGIOCTL)
printk("quickcam_ioctl: VIDIOCGMBUF\n");
memset(&vm, 0, sizeof(vm));
vm.size = MAX_FRAME_SIZE * 2;
vm.frames = 2;
vm.offsets[0] = 0;
vm.offsets[1] = MAX_FRAME_SIZE;
if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
retval = -EFAULT;
break;
}
case VIDIOCMCAPTURE:
{
struct video_mmap vm;
if (debug&DEBUGIOCTL)
printk("quickcam_ioctl: VIDIOCMCAPTURE\n");
if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm))) {
retval = -EFAULT;
break;
}
if (debug&DEBUGIOCTL)
printk("quickcam_ioctl: frame: %d, size: %dx%d, format: %d\n",
vm.frame, vm.width, vm.height, vm.format);
/* check supported formats */
retval = IsSupported(vm.format);
if (retval)
break; /* format not supported */
quickcam->vpic.palette = vm.format;
if ((vm.frame != 0) && (vm.frame != 1)) {
retval = -EINVAL;
break;
}
if (quickcam->frame[vm.frame].grabstate == FRAME_GRABBING) {
//printk("frame: %d, FRAME_GRABBING\n", vm.frame);
retval = -EBUSY;
break;
}
quickcam->frame[vm.frame].width = vm.width;
quickcam->frame[vm.frame].height = vm.height;
/* Mark it as ready */
quickcam->frame[vm.frame].grabstate = FRAME_READY;
retval = quickcam_new_frame(quickcam, vm.frame);
break;
}
case VIDIOCSYNC:
{
int frame,retry;
if (copy_from_user((void *)&frame, arg, sizeof(int))) {
retval = -EFAULT;
break;
}
if (debug&DEBUGIOCTL)
printk("quickcam_ioctl: VIDIOCSYNC for %d (%d)\n", frame,quickcam->frame[frame].grabstate);
if (debug&DEBUGIOCTL)
printk("quickcam_ioctl: VIDIOCSYNC to frame %d\n", frame);
for (retry=0;retry<3;retry++) {
if (!quickcam->dev) {
retval = -EIO;
break;
}
/* Check that CAPTURE was done for this frame */
if (quickcam->frame[frame].grabstate==FRAME_UNUSED) {
retval = -EINVAL;
break;
}
/* if error we retry */
else if (quickcam->frame[frame].grabstate==FRAME_ERROR) {
if (debug&DEBUGIOCTL)
printk("quickcam_ioctl: error retrying!\n");
/*
if (usb_quickcam_set1(quickcam->dev, STV_ISO_ENABLE, 0)<0) {
printk("usb_quickcam_set1 failed!\n");
retval = -EBUSY;
break;
}
*/
quickcam->scratchlen = 0; // discard buffer.
if ((retval = usb_quickcam_upload_frame(quickcam)) < 0)
break;
quickcam->frame[frame].grabstate = FRAME_GRABBING;
quickcam->frame[frame].scanlength = 0;
}
else if (quickcam->frame[frame].grabstate==FRAME_DONE) {
break;
}
else {
do {
interruptible_sleep_on(&quickcam->frame[frame].wq);
if (signal_pending(current)) {
if (debug&DEBUGIOCTL)
printk("quickcam_ioctl: VIDIOCSYNC alarmed (%d:%d)\n",quickcam->frame[frame].grabstate,quickcam->scanstate);
/* try JFC */
quickcam->frame[frame].grabstate = FRAME_ERROR;
quickcam->scanstate = STATE_ERROR;
quickcam->scratchlen = 0;
quickcam->grabbing = 0; // I am looking another to detect it.
retval = -EINTR;
break;
}
} while (quickcam->frame[frame].grabstate == FRAME_GRABBING ||
quickcam->frame[frame].grabstate == FRAME_READY);
if (retval) break;
}
}
/* too many errors, return error */
if (retry==3 && quickcam->frame[frame].grabstate == FRAME_ERROR) {
retval = -EBUSY;
break;
}
/*
* stop isostream if we have all what we need.
*/
if (quickcam->curframe==-1) {
usb_quickcam_stop(quickcam);
quickcam->grabbing = 0;
}
/* Convert the Bayer to RBG24 and set gains & exposure */
quickcam_parse_data(quickcam,frame);
if (quickcam->vpic.palette!=VIDEO_PALETTE_RGB24)
quickcam_convert_image(&quickcam->frame[frame],quickcam->vpic.palette);
if (debug&DEBUGIOCTL)
printk("quickcam_ioctl: VIDIOCSYNC got %d (%d) RC:%d)\n", frame,quickcam->frame[frame].grabstate,retval);
quickcam->frame[frame].grabstate = FRAME_UNUSED;
break;
}
case VIDIOCGFBUF:
{
struct video_buffer vb;
if (debug&DEBUGIOCTL)
printk("quickcam_ioctl: VIDIOCGFBUF\n");
memset(&vb, 0, sizeof(vb));
vb.base = NULL; /* frame buffer not supported, not used */
if (copy_to_user((void *)arg, (void *)&vb, sizeof(vb)))
retval = -EFAULT;
break;
}
case VIDIOCKEY:
break;
case VIDIOCCAPTURE:
retval = -EINVAL;
break;
case VIDIOCSFBUF:
retval = -EINVAL;
break;
case VIDIOCGTUNER:
case VIDIOCSTUNER:
retval = -EINVAL;
break;
case VIDIOCGFREQ:
case VIDIOCSFREQ:
retval = -EINVAL;
break;
case VIDIOCGAUDIO:
case VIDIOCSAUDIO:
retval = -EINVAL;
break;
default:
retval = -ENOIOCTLCMD;
break;
}
up(&quickcam->busy_lock);
return retval;
}
static long quickcam_read(struct video_device *dev, char *buf, unsigned long count, int noblock)
{
struct usb_quickcam *quickcam = (struct usb_quickcam *)dev;
int frmx = -1;
struct quickcam_frame *frame;
int retry=0;
if (debug&DEBUGREAD)
printk("quickcam_read: %ld bytes, noblock=%d\n", count, noblock);
if (!dev || !buf)
{
if (debug&DEBUGREAD)
printk("quickcam_read: no video_device available or no buffer attached :( EFAULT\n");
return -EFAULT;
}
if (!quickcam->dev)
{
if (debug&DEBUGREAD)
printk("quickcam_read: no quickcam_device available :( EIO\n");
return -EIO;
}
if (down_interruptible(&quickcam->busy_lock))
{
if (debug&DEBUGREAD)
printk("quickcam_read: quickcam_device busy :( EINTR\n");
return -EINTR;
}
/* check if we have already started to read */
if (quickcam->readframe!=-1) {
frmx = quickcam->readframe; // well I could have put a goto here...
frame = &quickcam->frame[frmx];
if (debug&DEBUGREAD)
printk("quickcam_read: we didn't start reading yet...start now\n");
goto read; // damned I have done it...
}
/* See if a frame is completed, then use it. */
if (frmx == - 1) {
if (debug&DEBUGREAD)
printk("quickcam_read: check if a frame is already completed... ");
if (quickcam->frame[0].grabstate >= FRAME_DONE) /* _DONE or _ERROR */
frmx = 0;
else if (quickcam->frame[1].grabstate >= FRAME_DONE)/* _DONE or _ERROR */
frmx = 1;
if (debug&DEBUGREAD) { /* Not mine jfclere */
if (frmx == -1)
printk("NO\n");
else
printk("YES\n");
}
}
if (noblock && (frmx == -1)) {
if (debug&DEBUGREAD)
printk("quickcam_read: blocked device :( EAGAIN\n");
up(&quickcam->busy_lock);
return -EAGAIN;
}
/* If no FRAME_DONE, look for a FRAME_GRABBING state. */
/* See if a frame is in process (grabbing), then use it. */
if (frmx == -1) {
if (debug&DEBUGREAD)
printk("quickcam_read: checking wheter something is grabbing at the moment... ");
if (quickcam->frame[0].grabstate == FRAME_GRABBING)
frmx = 0;
else if (quickcam->frame[1].grabstate == FRAME_GRABBING)
frmx = 1;
if (debug&DEBUGREAD) { /* Not mine jfclere */
if (frmx == -1)
printk("NO\n");
else
printk("YES\n");
}
}
/* If no frame is active, start one. */
if (frmx == -1) {
if (debug&DEBUGREAD)
printk("quickcam_read: no active frames....start new one\n");
/* grabbing or not grabbing */
if (!quickcam->grabbing || quickcam->curframe==-1) {
if (debug&DEBUGREAD)
printk("quickcam_read: we are not grabbing....start it\n");
quickcam_new_frame(quickcam, 0);
quickcam_new_frame(quickcam, 1); /* allow double buffering */
}
if (debug&DEBUGREAD)
printk("quickcam_read: waiting for the frame to finish...\n");
frmx = quickcam->curframe; /* we have to wait for the one in progress */
}
if (frmx == -1)
frmx = 0;
frame = &quickcam->frame[frmx];
if (debug&DEBUGREAD)
printk("quickcam_read: using %d state %d\n", frmx, frame->grabstate);
restart:
if (debug&DEBUGREAD)
printk("quickcam_read: goto restart called\n");
if (!quickcam->dev) {
if (debug&DEBUGREAD)
printk("quickcam_read: no quickcam_device available :( EIO\n");
up(&quickcam->busy_lock);
return -EIO;
}
while (frame->grabstate == FRAME_READY ||
frame->grabstate == FRAME_GRABBING) {
//if (debug&DEBUGREAD)
//printk("quickcam_read: in FRAME_READY || FRAME_GRABBING loop ...waiting\n");
interruptible_sleep_on(&frame->wq);
if (signal_pending(current)) {
if (debug&DEBUGREAD)
printk("quickcam_read: aborting... :( EINTR\n");
up(&quickcam->busy_lock);
return -EINTR;
}
}
if (frame->grabstate != FRAME_DONE) {
frame->bytes_read = 0;
if (debug&DEBUGREAD)
printk("quickcam_read: errored frame %d\n", quickcam->curframe);
if (quickcam_new_frame(quickcam, frmx))
{ if (debug&DEBUGREAD)
printk("quickcam_read: quickcam_new_frame error\n");
}
retry++;
if (retry<=3)
goto restart;
up(&quickcam->busy_lock);
return -EINTR;
}
/* Convert the Bayer to RBG24 and set gains & exposure */
quickcam_parse_data(quickcam,frmx);
if (quickcam->vpic.palette!=VIDEO_PALETTE_RGB24)
quickcam_convert_image(&quickcam->frame[frmx],quickcam->vpic.palette);
read:
if (debug&DEBUGREAD)
printk("quickcam_read: frmx=%d, bytes_read=%ld, scanlength=%ld\n",
frmx, frame->bytes_read, frame->scanlength);
/* copy bytes to user space; we allow for partials reads */
quickcam->readframe = frmx;
if ((count + frame->bytes_read) > frame->scanlength)
count = frame->scanlength - frame->bytes_read;
if(copy_to_user(buf, frame->data + frame->bytes_read, count)) {
up(&quickcam->busy_lock);
return -EFAULT;
}
frame->bytes_read += count;
if (debug&DEBUGREAD)
printk("quickcam_read: {copy} count used=%ld, new bytes_read=%ld\n",
count, frame->bytes_read);
if (frame->bytes_read >= frame->scanlength) { /* All data has been read */
frame->bytes_read = 0;
quickcam->readframe = -1; // we have finished.
/* Mark it as available to be used again. */
quickcam->frame[frmx].grabstate = FRAME_UNUSED;
if (quickcam_new_frame(quickcam, frmx))
printk(KERN_ERR "quickcam_read: quickcam_new_frame returned error\n");
}
up(&quickcam->busy_lock);
return count;
}
static int quickcam_mmap(struct video_device *dev, const char *adr, unsigned long size)
{
struct usb_quickcam *quickcam = (struct usb_quickcam *)dev;
unsigned long start = (unsigned long)adr;
unsigned long page, pos;
if (!quickcam->dev)
return -EIO;
if (size > (((2 * MAX_FRAME_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))
return -EINVAL;
if (down_interruptible(&quickcam->busy_lock))
return -EINTR;
pos = (unsigned long)quickcam->fbuf;
while (size > 0) {
page = kvirt_to_pa(pos);
if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) {
up(&quickcam->busy_lock);
return -EAGAIN;
}
start += PAGE_SIZE;
pos += PAGE_SIZE;
if (size > PAGE_SIZE)
size -= PAGE_SIZE;
else
size = 0;
}
up(&quickcam->busy_lock);
return 0;
}
static struct video_device quickcam_template = {
name: "Logitech Quickcam Express USB",
type: VID_TYPE_CAPTURE,
hardware: VID_HARDWARE_QCE,
initialize: quickcam_init_done,
open: quickcam_open,
close: quickcam_close,
read: quickcam_read,
write: quickcam_write,
ioctl: quickcam_ioctl,
mmap: quickcam_mmap,
};
static int usb_quickcam_configure(struct usb_quickcam *quickcam)
{
struct usb_device *dev = quickcam->dev;
unsigned char i2c[1];
unsigned char i2c2[2];
/* set default values for the camera */
quickcam->shutter_val=SHUTTER_VAL;
quickcam->gain=GAIN_VAL;
quickcam->scanstate = STATE_OK;
quickcam->x=quickcam->yy=0;
quickcam->readframe = -1;
/* Set altsetting 0 */
if (usb_set_interface(dev, quickcam->iface, 0) < 0) {
printk("usb_set_interface error\n");
return -EBUSY;
}
if (usb_quickcam_set1(dev, STV_ISO_ENABLE, 0) < 0) {
printk("usb_quickcam_set1 STV_ISO_ENABLE(0) failed\n");
return -EBUSY;
}
/* Set quickcam i2c for 8-bit regs */
if (usb_quickcam_set1(dev, STV_REG23, 0) < 0) {
printk("usb_quickcam_set1 STV_REG23(0) failed\n");
goto error;
}
/* Check I2C */
if (usb_quickcam_get_i2c(dev, i2c) < 0) {
printk("quickcam_get_i2c error\n");
return -EBUSY;
}
printk("quickcam: HDCS1000-sensor ident:%02x\n", i2c[0]);
if (i2c[0]==8) {
printk("This Quickcam has a HDCS1000 sensor!\n");
quickcam->sensor_addr = HDCS_ADDR;
load_hdcs_mod(&quickcam->sensor_ctrl);
}
else {
/* Check for Photobit */
if (usb_quickcam_set1(dev, STV_REG23, 1) < 0)
goto error;
if (usb_quickcam_get_i2c2(dev, i2c2) < 0) {
printk("quickcam_get_i2c error\n");
return -EBUSY;
}
printk("quickcam: Photobit-sensor ident:%02x %02x\n",
i2c2[0],i2c2[1]);
if (i2c2[1]==0x64) {
printk("This Quickcam has a PB100 sensor!\n");
quickcam->sensor_addr = PB_ADDR;
load_pb0100_mod(&quickcam->sensor_ctrl);
} else {
printk("unsupported sensor!\n");
return -EBUSY;
}
}
memcpy(&quickcam->vdev, &quickcam_template, sizeof(quickcam_template));
init_waitqueue_head(&quickcam->frame[0].wq);
init_waitqueue_head(&quickcam->frame[1].wq);
#if LINUX_VERSION_CODE >= 0x020405
if (video_register_device(&quickcam->vdev, VFL_TYPE_GRABBER, video_nr) == -1)
#else
if (video_register_device(&quickcam->vdev, VFL_TYPE_GRABBER) == -1)
#endif
{
printk("video_register_device failed\n");
return -EBUSY;
}
// Disable data stream.
if (usb_quickcam_set1(dev, STV_ISO_ENABLE, 0) < 0) {
printk("usb_quickcam_set1 STV_ISO_ENABLE(1) failed\n");
goto error;
}
if (usb_quickcam_set1(dev, STV_REG23, 1) < 0) {
/* I see no reason that the others failed if this one is OK */
printk("usb_quickcam_set1 STV_REG23(1) failed\n");
goto error;
}
// Setup internal video_picture
quickcam->vpic.colour = 0x8000;
quickcam->vpic.hue = 0x8000;
quickcam->vpic.brightness = 180 * 256;
quickcam->vpic.contrast = 128 * 256;
quickcam->vpic.whiteness = 0;
quickcam->vpic.palette = VIDEO_PALETTE_RGB24;
quickcam->vpic.depth = quickcam_get_depth(quickcam);
// Setup internal video_window
quickcam->vwin.x = 0;
quickcam->vwin.y = 0;
quickcam->vwin.chromakey = 0;
quickcam->vwin.flags = 30; /* 30 fps */
return 0;
error:
video_unregister_device(&quickcam->vdev);
usb_driver_release_interface(&quickcam_driver,
&dev->actconfig->interface[0]);
return -EBUSY;
}
#if LINUX_VERSION_CODE >= 0x020400
static void *quickcam_probe(struct usb_device *dev,
unsigned int ifnum, const struct usb_device_id *id)
#else
static void *quickcam_probe(struct usb_device *dev,
unsigned int ifnum)
#endif
{
struct usb_interface_descriptor *interface;
struct usb_quickcam *quickcam;
/* We don't handle multi-config cameras */
if (dev->descriptor.bNumConfigurations != 1)
return NULL;
interface = &dev->actconfig->interface[ifnum].altsetting[0];
/* Is it a Quickcam? */
printk("USB testing Class %x SubClass %x\n",interface->bInterfaceClass, interface->bInterfaceSubClass);
#if LINUX_VERSION_CODE < 0x0202FF
if (dev->descriptor.idVendor != 0x046d)
return NULL;
if (dev->descriptor.idProduct != 0x0870)
return NULL;
/* Checking vendor/product should be enough, but what the hell */
if (interface->bInterfaceClass != 0xFF)
return NULL;
if (interface->bInterfaceSubClass != 0xFF)
return NULL;
#endif
/* We found a Quickcam */
printk("USB Quickcam camera found using: %s\n", VERSION);
if ((quickcam = kmalloc(sizeof(*quickcam), GFP_KERNEL)) == NULL) {
printk("couldn't kmalloc quickcam struct\n");
return NULL;
}
memset(quickcam, 0, sizeof(*quickcam));
quickcam->dev = dev;
quickcam->iface = interface->bInterfaceNumber;
if (!usb_quickcam_configure(quickcam)) {
quickcam->user=0;
init_MUTEX(&quickcam->lock); /* to 1 == available */
init_MUTEX(&quickcam->busy_lock); /* to 1 == available */
return quickcam;
} else {
kfree(quickcam);
return NULL;
}
}
static void quickcam_disconnect(struct usb_device *dev, void *ptr)
{
struct usb_quickcam *quickcam = (struct usb_quickcam *) ptr;
if (!quickcam)
return;
/* We don't want people trying to open up the device */
if (!quickcam->user)
video_unregister_device(&quickcam->vdev);
usb_driver_release_interface(&quickcam_driver,
&quickcam->dev->actconfig->interface[0]);
quickcam->dev = NULL;
#if LINUX_VERSION_CODE >= 0x02040
#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
destroy_proc_quickcam(quickcam);
#endif
#endif
quickcam->frame[0].grabstate = FRAME_ERROR;
quickcam->frame[1].grabstate = FRAME_ERROR;
quickcam->curframe = -1;
/* This will cause the process to request another frame. */
if (waitqueue_active(&quickcam->frame[0].wq))
wake_up_interruptible(&quickcam->frame[0].wq);
if (waitqueue_active(&quickcam->frame[1].wq))
wake_up_interruptible(&quickcam->frame[1].wq);
quickcam->streaming = 0;
/* Unschedule all of the iso td's */
if (quickcam->sbuf[1].urb) {
quickcam->sbuf[1].urb->next = NULL;
usb_unlink_urb(quickcam->sbuf[1].urb);
usb_free_urb(quickcam->sbuf[1].urb);
quickcam->sbuf[1].urb = NULL;
}
if (quickcam->sbuf[0].urb) {
quickcam->sbuf[0].urb->next = NULL;
usb_unlink_urb(quickcam->sbuf[0].urb);
usb_free_urb(quickcam->sbuf[0].urb);
quickcam->sbuf[0].urb = NULL;
}
/* Free the memory */
if (!quickcam->user)
kfree(quickcam);
}
static struct usb_driver quickcam_driver = {
name: "quickcam",
#if LINUX_VERSION_CODE >= 0x020400
id_table: device_table,
#endif
probe: quickcam_probe,
disconnect: quickcam_disconnect,
};
static int __init usb_quickcam_init(void)
{
#if LINUX_VERSION_CODE >= 0x020400
#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
proc_quickcam_create();
#endif
#endif
return usb_register(&quickcam_driver);
}
static void __exit usb_quickcam_cleanup(void)
{
#if LINUX_VERSION_CODE >= 0x020400
#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
proc_quickcam_destroy();
#endif
#endif
usb_deregister(&quickcam_driver);
}
module_init(usb_quickcam_init);
module_exit(usb_quickcam_cleanup);