/* * qce-ga, linux V4L driver for the Quickcam Express and Dexxa Quickcam * * quickcam.c - main driver part * * Copyright (C) 2001 Jean-Fredric Clere,Nikolas Zimmermann, Georg Acher * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland * * 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 P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 (Dexxa WebCam USB not _yet_ supported) For any questions ask qce-ga-devel@lists.sourceforge.net! */ #if 0 /* Cuted by *BSD patch */ #include #include #include #include #include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) #include #else #include #endif #include #include #include #include #include #include #include #include #include #include #include #endif /* Cuted by *BSD patch */ #include #include #include #include #include #include #include #include "quickcam.h" #include "pb0100.h" #include "hdcs.h" #include "vv6410.h" /*#include "memory.h"*//* Cuted by *BSD patch */ #include "helper.h" #include "linux_usbif.h" #ifndef MODULE #define DEBUGLEVEL 0 // put in your wanted value here #endif #ifdef MODULE #define DEBUGLEVEL 0 MODULE_PARM(debug, "i"); MODULE_PARM(interpolation, "i"); MODULE_PARM(mode, "i"); MODULE_PARM(keepexposure, "i"); MODULE_PARM(tobgr, "i"); MODULE_PARM(rgain, "i"); MODULE_PARM(bgain, "i"); MODULE_PARM(ggain, "i"); MODULE_PARM(bright,"i"); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,5) MODULE_PARM(video_nr,"i"); #endif MODULE_PARM_DESC(debug, "Sets the debug output (1,2,4,8,16,32)"); MODULE_PARM_DESC(interpolation, "Sets the interpolation mode (0-1)"); MODULE_PARM_DESC(mode, "Sets the speed (0-1)"); MODULE_PARM_DESC(keepexposure, "Keep gain settings across one open to another (0-1)"); MODULE_PARM_DESC(tobgr, "Automatic RGB -> BGR conversion"); 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(bright,"Initial value for brightness (0-65535)"); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,5) MODULE_PARM_DESC(video_nr, "Set videodevice number (/dev/videoX)"); #endif MODULE_SUPPORTED_DEVICE("video"); MODULE_DESCRIPTION("Logitech Quickcam Express Webcam driver"); MODULE_AUTHOR("see README"); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,9) #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #endif #endif #endif static int debug = DEBUGLEVEL; static int keepexposure = 0; int tobgr = 1; int interpolation = 1; /* 1 = bilinear interpolation */ 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; static int bright= 32768; /* Zero gives black images with gnomemeeting */ #if 0 /* Cuted by *BSD patch */ /* 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; #endif /* Cuted by *BSD patch */ /* Video Size 352 x 288 x 4 bytes for 0RGB 32 bpp mode */ #define MAX_FRAME_SIZE (352 * 288 * 4) #define VERSION "$Id: quickcam.c,v 1.111 2003/01/27 09:41:03 tuukkat Exp $" " + BSD patch 0.04" #if 0 /* Cuted by *BSD patch */ static struct usb_driver quickcam_driver; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) static __devinitdata struct usb_device_id device_table [] = { { USB_DEVICE(0x046d, 0x0840) }, /* Quickcam Express */ { USB_DEVICE(0x046d, 0x0850) }, /* LEGO cam - not yet supported */ { USB_DEVICE(0x046d, 0x0870) }, /* Dexxa webcam USB */ { } }; MODULE_DEVICE_TABLE (usb, device_table); #endif #endif /* Cuted by *BSD patch */ static struct sensor_data sensors [] = { /* name reg23 i2c_addr id_reg id l load */ /* ---- ----- ----------- -------------- ---- - -------------- */ { "HDCS1000", 0, HDCS_ADDR, HDCS_IDENT + 1, 0x08, 1, load_hdcs_mod }, { "BP100", 1, PB_ADDR, PB_IDENT, 0x64, 2, load_pb0100_mod }, { "VV6410", 5, VV6410_ADDR, VV6410_IDENT, 0x19, 1, load_vv6410_mod }, { "HDCS1020", 0, HDCS_ADDR, HDCS_IDENT + 1, 0x10, 1, load_hdcs20_mod }, { NULL } }; #if 0 /* Cuted by *BSD patch */ /* * 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); } #endif /* Cuted by *BSD patch */ /* * /proc interface for our driver */ #if 0 /* Cuted by *BSD patch */ #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", dev->sensor_name); 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, "\n*** Sensor Control ***\n"); out += sprintf(out, "Shutter value : %d\n", dev->shutter_val); out += sprintf(out, "Gain : %d\n", dev->gain); out += sprintf(out, "Exposure value : %ld\n", dev->val); out += sprintf(out, "Red Gain : %d\n", dev->red); out += sprintf(out, "Green Gain : %d\n", dev->green); out += sprintf(out, "Blue Gain : %d\n", dev->blue); out += sprintf(out, "\n*** Output Window ***\n"); out += sprintf(out, "Width : %d\n", dev->vwin.width); out += sprintf(out, "Height : %d\n", dev->vwin.height); out += sprintf(out, "\n*** 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[9]; 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[9]; 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 /* Cuted by *BSD patch */ /* * 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) { unsigned 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, 0, 0x04, 0x40, 0x1400, 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, unsigned char sensor_addr, int reg, void *buf, int len) { if (usb_quickcam_i2c_in(dev,reg,sensor_addr)<0) { fprintf(stderr, "usb_quickcam_i2c_in failed\n"); return(-1); } return usb_control_msg(dev, 0, 0x04, 0xC0, 0x1410, 0, buf, len, HZ); } /* * Set register one byte */ int usb_quickcam_set1(struct usb_device *dev, unsigned short reg, unsigned char val) { unsigned char buff[1]; buff[0] = val; return usb_control_msg(dev, 0, 0x04, 0x40, reg, 0, buff, 1, HZ); } /* * Set register two byte */ int usb_quickcam_set2(struct usb_device *dev, unsigned short reg, unsigned short val) { unsigned char buff[2]; buff[0] = val&0xFF; buff[1] = (val>>8)&255; return usb_control_msg(dev, 0, 0x04, 0x40, reg, 0, buff, 2, HZ); } /* Send a command to the sensor */ static int quickcam_usb_control_msg(struct usb_device *dev, unsigned char *buff) { return usb_control_msg(dev, 0, 0x04, 0x40, 0x400, 0, buff, 0x23 , HZ); } /** * Clean buffer for I2C messages. */ void usb_quickcam_i2c_new(struct quickcam_i2c *i2cbuff) { i2cbuff->length=0; memset(i2cbuff->buff,'\0',0x23); } /** * Add register and byte value. */ void usb_quickcam_i2c_add(struct quickcam_i2c *i2cbuff, unsigned char reg, unsigned char value) { i2cbuff->buff[i2cbuff->length] = reg; i2cbuff->buff[i2cbuff->length+0x10] = value; i2cbuff->length++; } /** * Add register and 2 bytes value. */ void usb_quickcam_i2c_add2(struct quickcam_i2c *i2cbuff, unsigned char reg, unsigned short value) { i2cbuff->buff[i2cbuff->length] = reg; i2cbuff->buff[(i2cbuff->length*2)+0x10] = value&255; i2cbuff->buff[(i2cbuff->length*2)+0x11] = (value>>8)&255; i2cbuff->length++; } /** * Send the I2C message. */ int usb_quickcam_i2c_send(struct usb_device *dev, struct quickcam_i2c *i2cbuff, unsigned char sensor_add) { int ret; i2cbuff->buff[0x20]=sensor_add; i2cbuff->buff[0x21]=(unsigned char)i2cbuff->length-1; i2cbuff->buff[0x22]=1; // Write cmd, 03 would be read. ret = quickcam_usb_control_msg(dev,i2cbuff->buff); usb_quickcam_i2c_new(i2cbuff); return(ret); } /* set the gains according to V4L default settings */ static int usb_quickcam_set_gains(struct usb_quickcam *quickcam) { struct usb_device *dev = quickcam->dev; int gr, gb, gg; gr=qcmin(255,qcmax(1,(quickcam->gain*quickcam->red)>>7)); gb=qcmin(255,qcmax(1,(quickcam->gain*quickcam->blue)>>7)); gg=qcmin(255,qcmax(1,(quickcam->gain*quickcam->green)>>7)); if (debug&DEBUGDATA) { fprintf(stderr,"usb_quickcam_set_gains: gain %d, red %d, blue %d, green %d\n", quickcam->gain,quickcam->red,quickcam->blue,quickcam->green); fprintf(stderr,"usb_quickcam_set_gains: gr %d, gb %d, gg %d\n",gr,gb,gg); } return quickcam->sensor_ctrl.set_gains(dev,gr,gb,gg); } /* * 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 (acher@in.tum.de) set_exposure(). * And he says: "Auto-Exposure needs a bit more intelligence"! */ int usb_quickcam_set_exposure(struct usb_quickcam *quickcam, long val) { int og=quickcam->gain; int os=quickcam->shutter_val; int brightness=quickcam->brightness>>8; struct usb_device *dev = quickcam->dev; quickcam->val = val; //printk("usb_quickcam_set_exposure: required %d, measured %ld\n",brightness,val); if (qcabs(val-brightness)>5) quickcam->gain-=(val-brightness)/5; quickcam->gain=qcmin(255,qcmax(1,quickcam->gain)); /* It seems that the less gain you have the better ... You have less noise */ if (quickcam->gain<=2 && quickcam->shutter_val>2) { quickcam->gain+=2; quickcam->shutter_val-=2; } /* Instead of amplifying the signal, open the shutter to get more light ! */ if (quickcam->gain>=200 && quickcam->shutter_val<254) { quickcam->gain-=2; quickcam->shutter_val+=2; } if (debug&DEBUGDATA) { fprintf(stderr,"usb_quickcam_set_exposure: required %d, mesured %ld\n",brightness,val); fprintf(stderr,"usb_quickcam_set_exposure: gain %d shutter %d\n",quickcam->gain,quickcam->shutter_val); } if (os!=quickcam->shutter_val) quickcam->sensor_ctrl.set_shutter(dev,quickcam->shutter_val,0x100); if (og!=quickcam->gain) return usb_quickcam_set_gains(quickcam); return 0; } /* Start camera and esable ISO-streaming *//* add */ int usb_quickcam_start(struct usb_quickcam *quickcam) { int ret=0; if (!quickcam->dev) return 0; // nothing to do. // start current frame. if ( quickcam->sensor_ctrl.start(quickcam->dev, &(quickcam->sensor_ctrl))<0) { ret = -1; } // start ISO-streaming. ret = usb_quickcam_set1(quickcam->dev, STV_ISO_ENABLE, 1); // ffprintf(stderr, "start current frame = %d\n",ret); if (debug&DEBUGLOGIC) fprintf(stderr,"usb_quickcam_start:%d\n",ret); return ret; } /* Stop camera and disable ISO-streaming */ 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, &(quickcam->sensor_ctrl)); if (debug&DEBUGLOGIC) fprintf(stderr,"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); #if 0 /* Cuted by *BSD patch */ static int usb_quickcam_upload_frame(struct usb_quickcam *quickcam) { if (debug&DEBUGLOGIC) fprintf(stderr,"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, &(quickcam->sensor_ctrl))<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 addgreen 1 #define addred 2 /* need cleaning ... */ #define XH quickcam->vwin.height #define XW quickcam->vwin.width #define SW quickcam->sensor_ctrl.width struct quickcam_frame *frame; long 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; int skiph; int startw, endw; /* calculate data to skip for the VV6410 sensor */ if (mode) { skiph = (quickcam->sensor_ctrl.height-(XH/2))/2; startw = (quickcam->sensor_ctrl.width-(XW/2))/2; } else { skiph = (quickcam->sensor_ctrl.height-XH)/2; startw = (quickcam->sensor_ctrl.width-XW)/2; } if (skiph<0) skiph=0; if (startw<0) startw=0; endw = quickcam->sensor_ctrl.width-startw; frame = &quickcam->frame[curframe]; if (debug&DEBUGDATA) printk("quickcam_parse_data %ld for frame %d\n", frame->storelength,curframe); /* Convert the current frame */ /* skip fisrt and last lines if needed */ data = frame->storedata+(skiph*quickcam->sensor_ctrl.width); for (i=skiph*quickcam->sensor_ctrl.width; istorelength-(skiph*quickcam->sensor_ctrl.width); i++) { /* * Skip line data when needed. */ if ((i%quickcam->sensor_ctrl.widthsensor_ctrl.width>=endw && startw)) { data++; continue; } /* * 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 (interpolation == 1) { /* bilinear interpolation */ if (!(yy&1)) { // even row if (xx&1) { // odd column, red *(o+addred)=*data; if (yy == 0) { if (xx == XW-1) { *(o+addblue)=*(data+SW-1); *(o+addgreen)=(*(data-1) + *(data+SW))/2; } else { *(o+addblue)=(*(data+SW-1) + *(data+SW+1))/2; *(o+addgreen)=(*(data-1) + *(data+1) + *(data+SW))/3; } } else { if (xx == XW-1) { *(o+addblue)=(*(data-SW-1) + *(data+SW-1))/2; *(o+addgreen)=(*(data-SW) + *(data-1) + *(data+SW))/3; } else { *(o+addblue)=(*(data-SW-1) + *(data-SW+1) + *(data+SW-1) + *(data+SW+1))/4; *(o+addgreen)=(*(data-SW) + *(data-1) + *(data+1) + *(data+SW))/4; } } if (mode) { } } else { // green if (yy == 0) { if (xx == 0) { *(o+addred)=*(data+1); *(o+addblue)=*(data+SW); } else { *(o+addred)=(*(data-1) + *(data+1))/2; *(o+addblue)=*(data+SW); } } else { if (xx == 0) { *(o+addred)=*(data+1); *(o+addblue)=(*(data-SW) + *(data+SW))/2; } else { *(o+addred)=(*(data-1) + *(data+1))/2; *(o+addblue)=(*(data-SW) + *(data+SW))/2; } } *(o+addgreen)=*data; if (mode) { } } } else { if (xx&1) { // odd column green if (yy == XH-1) { if (xx == XW-1) { *(o+addred)=*(data-SW); *(o+addblue)=*(data-1); } else { *(o+addred)=*(data-SW); *(o+addblue)=(*(data-1) + *(data+1))/2; } } else { if (xx == XW-1) { *(o+addred)=(*(data-SW) + *(data+SW))/2; *(o+addblue)=*(data-1); } else { *(o+addred)=(*(data-SW) + *(data+SW))/2; *(o+addblue)=(*(data-1) + *(data+1))/2; } } *(o+addgreen)=*data; if (mode) { } } else { //blue *(o+addblue)=*data; if (yy == XH-1) { if (xx == 0) { *(o+addred)=*(data-SW+1); *(o+addgreen)=(*(data-SW) + *(data+1))/2; } else { *(o+addred)=(*(data-SW-1) + *(data-SW+1))/2; *(o+addgreen)=(*(data-SW) + *(data-1) + *(data+1))/3; } } else { if (xx == 0) { *(o+addred)=(*(data-SW+1) + *(data+SW+1))/2; *(o+addgreen)=(*(data-SW) + *(data+1) + *(data+SW))/3; } else { *(o+addred)=(*(data-SW-1) + *(data-SW+1) + *(data+SW-1) + *(data+SW+1))/4; *(o+addgreen)=(*(data-SW) + *(data-1) + *(data+1) + *(data+SW))/4; } } if (mode) { } } } } else { /* not bilinear interpolation */ 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 || (yy>=XH/2 && mode)) { if (quickcam->sensor_ctrl.mode==2) mid_value = mid_value/(yy/2); else mid_value = mid_value/yy; usb_quickcam_set_exposure(quickcam,mid_value); } /* if mode=2 we have only 1/2 of the enlargement */ if (quickcam->sensor_ctrl.mode==2) { data = frame->data; for (i=XH/2;i>1;i--) { memcpy(&data[i*6*XW],&data[i*3*XW],XW*3); memcpy(&data[i*6*XW-3*XW],&data[i*3*XW],XW*3); } // memcpy(&data[XW*6],&data,XW*3); copylen = copylen *2; } if (mode) copylen = copylen *2; frame->scanlength = (copylen*6)/2; // yes but how to compute partial pixels!. /* RGB -> BGR */ if(tobgr) { char c; char *p = frame->data; int i = quickcam->vwin.width * quickcam->vwin.height; while(--i) { c = p[0]; p[0] = p[2]; p[2] = c; p += 3; } } } /* * Analyse the data and store it * This routine has been written by Carlo E. Prelz, fluido@fluido.as * 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; } /* Use sensor information to get the framesize */ if (quickcam->sensor_ctrl.mode==1) framesize = qcmin(BAYER_FRAME_SIZE, (quickcam->sensor_ctrl.width* quickcam->sensor_ctrl.height)*2); else framesize = qcmin(BAYER_FRAME_SIZE, quickcam->sensor_ctrl.width* quickcam->sensor_ctrl.height); /* Check the length */ for(i=0,tot_len=quickcam->scratchlen;inumber_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;inumber_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(pframe) { if (debug&DEBUGINT) printk("quickcam: storing %d already in %ld\n",frag_len,pframe->storelength); 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.). * */ if(pframe) printk("quickcam got garbage at %ld\n",pframe->storelength); else printk("quickcam got a garbage\n"); left = tot_len - cur_len; left = qcmin(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; } #endif /* Cuted by *BSD patch */ /* * Start isoc */ static int quickcam_init_isoc(struct usb_quickcam *quickcam) { if (debug&DEBUGLOGIC) fprintf(stderr,"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) { fprintf(stderr,"usb_set_interface error\n"); return -EBUSY; } #if 0 /* Cuted by *BSD patch */ /* 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; #endif /* Cuted by *BSD patch */ if (debug&DEBUGLOGIC) fprintf(stderr,"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) { fprintf(stderr,"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, &(quickcam->sensor_ctrl))<0) { fprintf(stderr,"set_window sensor failed\n"); return(-1); } 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) fprintf(stderr, "quickcam_init_sensor: init sensor\n"); if (quickcam->sensor_ctrl.init(quickcam->dev,mode, &rgain, &bgain, &ggain, &(quickcam->sensor_ctrl))<0) { fprintf(stderr,"Init sensor failed\n"); return(-1); } /* gain is the average of blue, green and red gain */ if(!keepexposure) { quickcam->gain =10; } quickcam->blue =qcmin(255,qcmax(2,bgain)); quickcam->red =qcmin(255,qcmax(2,rgain)); quickcam->green=qcmin(255,qcmax(2,ggain)); if (usb_quickcam_set_gains(quickcam)<0) { fprintf(stderr,"set_gains sensor failed\n"); return(-1); } if (quickcam->sensor_ctrl.set_shutter(quickcam->dev,quickcam->shutter_val,0x100)<0) { fprintf(stderr,"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) fprintf(stderr, "quickcam_stop_isoc\n"); /* Turn off continuous grab */ if (usb_quickcam_stop(quickcam) < 0) { fprintf(stderr,"usb_quickcam_stop error\n"); return -EBUSY; } /* Set packet size to 0 */ if (usb_set_interface(quickcam->dev, quickcam->iface, 0) < 0) { fprintf(stderr,"usb_set_interface error\n"); return -EINVAL; } #if 0 /* Cuted by *BSD patch */ 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; } #endif /* Cuted by *BSD patch */ return 0; } #if 0 /* Cuted by *BSD patch */ /* * 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 >= KERNEL_VERSION(2,4,0) #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_RGB32: return 32; case VIDEO_PALETTE_RGB24: return 24; case VIDEO_PALETTE_YUV420: return 24; case VIDEO_PALETTE_YUV422: return 16; case VIDEO_PALETTE_YUYV: return 16; case VIDEO_PALETTE_RGB565: return 16; case VIDEO_PALETTE_RGB555: return 15; default: return -EINVAL; } } #endif /* Cuted by *BSD patch */ /* Video 4 Linux API */ int quickcam_open(struct usb_quickcam *quickcam) { int err = -EBUSY; if (debug&DEBUGLOGIC) fprintf(stderr,"quickcam_open\n"); /* 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_sensor; if (debug&DEBUGLOGIC) fprintf(stderr,"quickcam_open successfull\n"); return 0; open_err_init_sensor: quickcam_stop_isoc(quickcam); open_err_init: if (debug&DEBUGLOGIC) fprintf(stderr,"quickcam_open failed\n"); return err; } void quickcam_close(struct usb_quickcam *quickcam) { if (debug&DEBUGLOGIC) fprintf(stderr,"quickcam_close\n"); quickcam_stop_isoc(quickcam); } #if 0 /* Cuted by *BSD patch */ 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"); quickcam->vpic.brightness = quickcam->brightness; quickcam->vpic.colour = quickcam->green<<8; quickcam->vpic.contrast = quickcam->shutter_val<<8; quickcam->vpic.hue = (quickcam->red - quickcam->blue + 0xff)<<7; 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 { if(p.brightness != quickcam->vpic.brightness) { quickcam->vpic.brightness = p.brightness; quickcam->brightness = qcmin(0xfd00,qcmax(0x100,quickcam->vpic.brightness)); } if(p.contrast != quickcam->vpic.contrast) { quickcam->vpic.contrast = p.contrast; quickcam->shutter_val = qcmin(0xfd,qcmax(1,quickcam->vpic.contrast>>8)); quickcam->sensor_ctrl.set_shutter(quickcam->dev,quickcam->shutter_val,0x100); } if(p.colour != quickcam->vpic.colour) { quickcam->vpic.colour = p.colour; quickcam->green = qcmin(0xfd,qcmax(1,quickcam->vpic.colour>>8)); usb_quickcam_set_gains(quickcam); } if(p.hue != quickcam->vpic.hue) { quickcam->vpic.hue = p.hue; quickcam->red = qcmin(0xfd,qcmax(1,quickcam->vpic.hue>>8)); quickcam->blue = 0xff-quickcam->red; usb_quickcam_set_gains(quickcam); } /* xawtv use this to find the supported format */ if (debug&DEBUGIOCTL) printk("quickcam_ioctl: VIDIOCSPICT depth: %d palette %d\n",p.depth, p.palette); 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 vw.flags %d vw.clipcount %d\n",vw.flags,vw.clipcount); if (copy_from_user(&vw, arg, sizeof(vw))) { retval = -EFAULT; break; } /* 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, }; #endif /* Cuted by *BSD patch */ static int usb_quickcam_configure(struct usb_quickcam *quickcam) { struct usb_device *dev = quickcam->dev; unsigned char id[2]; struct sensor_data *sensor; /* Set altsetting 0 */ if (usb_set_interface(dev, quickcam->iface, 0) < 0) { fprintf(stderr,"usb_set_interface error\n"); return -EBUSY; } if (usb_quickcam_set1(dev, STV_ISO_ENABLE, 0) < 0) { fprintf(stderr,"usb_quickcam_set1 STV_ISO_ENABLE(0) failed\n"); return -EBUSY; } /* Probe for the sensor type. */ for (sensor = sensors; sensor->name; sensor++) { if (usb_quickcam_set1(dev, STV_REG23, sensor->reg23) < 0) { fprintf(stderr,"usb_quickcam_set1 STV_REG23 failed\n"); return -EBUSY; } if (usb_quickcam_get_i2c(dev, sensor->i2c_addr, sensor->id_reg, id, sensor->length_id) < 0) { fprintf(stderr,"usb_quickcam_get_i2c error\n"); return -EBUSY; } fprintf(stderr,"quickcam: probe of %s sensor = %02x %02x id: %02x\n", sensor->name, id[0], id[1],sensor->id); if (id[sensor->length_id-1] == sensor->id) break; } if (!sensor->name) { fprintf(stderr,"quickcam: unsupported sensor\n"); return -EBUSY; } fprintf(stderr,"quickcam: %s sensor detected\n", sensor->name); quickcam->sensor_name = sensor->name; quickcam->sensor_addr = sensor->i2c_addr; sensor->load(&quickcam->sensor_ctrl); // Disable data stream. if (usb_quickcam_set1(dev, STV_ISO_ENABLE, 0) < 0) { fprintf(stderr,"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 */ fprintf(stderr,"usb_quickcam_set1 STV_REG23(1) failed\n"); goto error; } /* set default values for the camera */ if(bright<=0 || bright>=65535) bright=EXPO_VAL; quickcam->brightness = bright; quickcam->shutter_val= SHUTTER_VAL; quickcam->x = quickcam->yy=0; return 0; error: return -EBUSY; } /* * probe for NetBSD/FreeBSD */ void *quickcam_probe(struct usb_device *dev) { usb_device_descriptor_t ddesc; struct usb_quickcam *quickcam; if (ioctl(dev->fd, USB_GET_DEVICE_DESC, &ddesc) < 0) { perror("USB_GET_DEVICE_DESC"); return NULL; } dev->descriptor.bNumConfigurations = ddesc.bNumConfigurations; /* We don't handle multi-config cameras */ if (dev->descriptor.bNumConfigurations != 1) return NULL; /* Is it a Quickcam? */ dev->descriptor.idVendor = UGETW(ddesc.idVendor); dev->descriptor.idProduct = UGETW(ddesc.idProduct); if (dev->descriptor.idVendor != 0x046d) return NULL; if (dev->descriptor.idProduct != 0x0840 && dev->descriptor.idProduct != 0x0850 && dev->descriptor.idProduct != 0x0870) return NULL; dev->interfacedesc.uid_config_index = USB_CURRENT_CONFIG_INDEX; dev->interfacedesc.uid_alt_index = USB_CURRENT_ALT_INDEX; if (ioctl(dev->fd, USB_GET_INTERFACE_DESC, &dev->interfacedesc) < 0) { perror("USB_GET_INTERFACE_DESC"); return NULL; } /* * Checking vendor/product is not enough * In case on Quickcam Web the audio is at class 1 and subclass 1/2. * ono /dev/dsp and one /dev/mixer */ if (dev->interfacedesc.uid_desc.bInterfaceClass != 0xFF) return NULL; if (dev->interfacedesc.uid_desc.bInterfaceSubClass != 0xFF) return NULL; /* We found a Quickcam */ fprintf(stderr,"USB Quickcam camera found using: %s\n", VERSION); if ((quickcam = malloc(sizeof(*quickcam))) == NULL) { perror("couldn't kmalloc quickcam struct\n"); return NULL; } memset(quickcam, 0, sizeof(*quickcam)); quickcam->dev = dev; quickcam->iface = dev->interfacedesc.uid_desc.bInterfaceNumber; /* not use */ if (usb_quickcam_configure(quickcam) == 0) { return quickcam; } else { free(quickcam); return NULL; } } #if 0 /* Cuted by *BSD patch */ 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 >= KERNEL_VERSION(2,4,0) #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 >= KERNEL_VERSION(2,4,0) id_table: device_table, #endif probe: quickcam_probe, disconnect: quickcam_disconnect, }; static int __init usb_quickcam_init(void) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) #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 >= KERNEL_VERSION(2,4,0) #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); #endif /* Cuted by *BSD patch */