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);