diff -Naur linux-2.4.0-test10-pre6-orig/Documentation/usb/ov511.txt linux/Documentation/usb/ov511.txt --- linux-2.4.0-test10-pre6-orig/Documentation/usb/ov511.txt Sun Oct 29 03:04:35 2000 +++ linux/Documentation/usb/ov511.txt Sun Oct 29 03:05:29 2000 @@ -5,13 +5,6 @@ Author: Mark McClelland Homepage: http://alpha.dyndns.org/ov511 -NEW IN THIS VERSION: - o Stability improvements - o Support for hue control - o 160x120 mostly working - o OV6620 color problems fixed - o More WebCam 3 detection improvements - INTRODUCTION: This is a driver for the OV511, a USB-only chip used in many "webcam" devices. @@ -203,15 +196,21 @@ DESC: Prevent apps from timing out if frame is not done in time. This is useful if you are having problems with Xawtv getting "stuck" on a frame when your system is under heavy load. - + + NAME: sensor_gbr + TYPE: boolean + DEFAULT: 0 + DESC: This makes the sensor output GBR422 instead of YUV420. This saves the + driver the trouble of converting YUV to RGB, but it currently does not + work very well (the colors are not quite right) + WORKING FEATURES: - o Color streaming/capture at 640x480, 448x336, 384x288, 352x288, 320x240, and - 160x120 + o Color streaming/capture at 640x480, 448x336, 384x288, 352x288, and 320x240 o RGB24, YUV420, YUV422, YUYV, and YUV422P color o Monochrome o Setting/getting of saturation, contrast, brightness, and hue (only some of them work the OV7620 and OV7620AE) - o proc status reporting + o /proc status reporting EXPERIMENTAL FEATURES: o fix_rgb_offset: Sometimes works, but other times causes errors with xawtv and @@ -219,6 +218,8 @@ o Snapshot mode (only works with some read() based apps; see below for more) o OV6620 sensor support o GBR422 parsing + o 160x120 + o RGB565 V4L format TODO: o Fix the noise / grainy image problem. @@ -227,18 +228,19 @@ so we can't really work on that yet. Please kindly inform OmniVision that you would like them to release their specifications to the Linux community. o YUV422 - o Get snapshot mode working with mmap(). o Fix fixFrameRGBoffset(). It is not stable yet with streaming video. - o Get autoadjust disable working o V4L2 support (Probably not until it goes into the kernel) - o Creative WebCam III has problems initializing its sensor. This should be - fixed now, but if you still have problems let me know. o Get rid of the memory management functions (put them in videodev.c??) o Setting of contrast and brightness not working with 7620/7620AE o Driver/camera state save/restore for when USB supports suspend/resume o Unstable on SMP systems o OV7620/OV6620 experience frame corruption with moving objects o OV6620 is too dark + o 176x144 support + o Driver sometimes hangs upon close() with OHCI + o The image should always be written properly to the mmap'ed buffer as long as + the requested image size is at least the minimum size. This will likely + require a rewrite of all the parsing code. HOW TO CONTACT ME: diff -Naur linux-2.4.0-test10-pre6-orig/drivers/usb/ov511.c linux/drivers/usb/ov511.c --- linux-2.4.0-test10-pre6-orig/drivers/usb/ov511.c Sun Oct 29 03:04:11 2000 +++ linux/drivers/usb/ov511.c Sun Oct 29 03:05:19 2000 @@ -30,7 +30,7 @@ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -static const char version[] = "1.25"; +static const char version[] = "1.27"; #define __NO_VERSION__ @@ -51,7 +51,9 @@ #include "ov511.h" -#undef OV511_GBR422 /* Experimental -- sets the 7610 to GBR422 */ +#undef OV511_CONTROL_API /* Experimental -- IOCTL control API hack */ + +#undef OV511_RGB565 /* Define to enable RGB565 (not fully working) */ #define OV511_I2C_RETRIES 3 @@ -61,7 +63,9 @@ #define GET_SEGSIZE(p) ((p) == VIDEO_PALETTE_GREY ? 256 : 384) #define GET_DEPTH(p) ((p) == VIDEO_PALETTE_GREY ? 8 : \ - ((p) == VIDEO_PALETTE_YUV422 ? 16 : 24)) + ((p) == VIDEO_PALETTE_YUV422 ? 16: \ + ((p) == VIDEO_PALETTE_YUYV ? 16: \ + ((p) == VIDEO_PALETTE_RGB565 ? 16: 24)))) /* PARAMETER VARIABLES: */ static int autoadjust = 1; /* CCD dynamically changes exposure, etc... */ @@ -74,7 +78,7 @@ * 5=highly repetitive mesgs * NOTE: This should be changed to 0, 1, or 2 for production kernels */ -static int debug = 3; +static int debug = 0; /* Fix vertical misalignment of red and blue at 640x480 */ static int fix_rgb_offset = 0; @@ -112,6 +116,10 @@ /* Display test pattern - doesn't work yet either */ static int testpat = 0; +/* Setting this to 1 will make the sensor output GBR422 instead on YUV420. Only + * affects RGB24 mode. */ +static int sensor_gbr = 0; + MODULE_PARM(autoadjust, "i"); MODULE_PARM_DESC(autoadjust, "CCD dynamically changes exposure"); MODULE_PARM(debug, "i"); @@ -138,6 +146,8 @@ MODULE_PARM_DESC(compress, "Turn on compression (not functional yet)"); MODULE_PARM(testpat, "i"); MODULE_PARM_DESC(testpat, "Replace image with vertical bar testpattern (only partially working)"); +MODULE_PARM(sensor_gbr, "i"); +MODULE_PARM_DESC(sensor_gbr, "Make sensor output GBR422 rather than YUV420"); MODULE_AUTHOR("Mark McClelland & Bret Wallach & Orion Sky Lawlor & Kevin Moore & Charl P. Botha & Claudio Matsuoka "); MODULE_DESCRIPTION("OV511 USB Camera Driver"); @@ -357,6 +367,7 @@ ov511->bridge == BRG_OV511PLUS ? "OV511+" : "unknown"); out += sprintf (out, "sensor : %s\n", + ov511->sensor == SEN_OV6620 ? "OV6620" : ov511->sensor == SEN_OV7610 ? "OV7610" : ov511->sensor == SEN_OV7620 ? "OV7620" : ov511->sensor == SEN_OV7620AE ? "OV7620AE" : @@ -448,7 +459,6 @@ } #endif /* CONFIG_PROC_FS && CONFIG_VIDEO_PROC_FS */ - /********************************************************************** * * Camera interface @@ -700,7 +710,7 @@ { int rc; - PDEBUG(3, "Reset: type=0x%X", reset_type); + PDEBUG(4, "Reset: type=0x%X", reset_type); rc = ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, reset_type); rc = ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0); @@ -829,14 +839,21 @@ } else if ((ov511->sensor == SEN_OV7620) || (ov511->sensor == SEN_OV7620AE)) { #if 0 - cur_con = ov511_i2c_read(dev, OV7610_REG_CNT); - cur_brt = ov511_i2c_read(dev, OV7610_REG_BRT); - // DEBUG_CODE - PDEBUG(1, "con=%d brt=%d", ov511_i2c_read(dev, OV7610_REG_CNT), - ov511_i2c_read(dev, OV7610_REG_BRT)); + int cur_sat, new_sat, tmp; - if (ov511_i2c_write(dev, OV7610_REG_CNT, p->contrast >> 8) < 0) + cur_sat = ov511_i2c_read(dev, OV7610_REG_BLUE); + + tmp = (p->hue >> 8) - cur_sat; + new_sat = (tmp < 0) ? (-tmp) | 0x80 : tmp; + + PDEBUG(1, "cur=%d target=%d diff=%d", cur_sat, p->hue >> 8, tmp); + + if (ov511_i2c_write(dev, OV7610_REG_BLUE, new_sat) < 0) return -EIO; + + // DEBUG_CODE + PDEBUG(1, "hue=%d", ov511_i2c_read(dev, OV7610_REG_BLUE)); + #endif } @@ -953,9 +970,9 @@ break; case SEN_OV6620: hwsbase = 0x38; - hwebase = 0x39; - vwsbase = 0x03; - vwebase = 0x04; + hwebase = 0x3a; + vwsbase = 0x05; + vwebase = 0x06; break; case SEN_OV7620: hwsbase = 0x2c; @@ -1039,13 +1056,16 @@ /* Calculate and set the clock divisor */ clock = ((sub_flag ? ov511->subw * ov511->subh : width * height) * (mlist[i].color ? 3 : 2) / 2) / 66000; +#if 0 + clock *= cams; +#endif ov511_i2c_write(dev, 0x11, clock); -#ifdef OV511_GBR422 - ov511_i2c_write(dev, 0x12, mlist[i].common_A | 0x08); -#else - ov511_i2c_write(dev, 0x12, mlist[i].common_A | (testpat?0x02:0x00)); -#endif + /* We only have code to convert GBR -> RGB24 */ + if ((mode == VIDEO_PALETTE_RGB24) && sensor_gbr) + ov511_i2c_write(dev, 0x12, mlist[i].common_A | (testpat?0x0a:0x08)); + else + ov511_i2c_write(dev, 0x12, mlist[i].common_A | (testpat?0x02:0x00)); /* 7620/6620 don't have register 0x35, so play it safe */ if (ov511->sensor == SEN_OV7610 || @@ -1113,7 +1133,7 @@ static inline void ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v, - int rowPixels, unsigned char * rgb) + int rowPixels, unsigned char * rgb, int bits) { const int rvScale = 91881; const int guScale = -22553; @@ -1134,14 +1154,29 @@ yTL *= yScale; yTR *= yScale; yBL *= yScale; yBR *= yScale; - /* Write out top two pixels */ - rgb[0] = LIMIT(b+yTL); rgb[1] = LIMIT(g+yTL); rgb[2] = LIMIT(r+yTL); - rgb[3] = LIMIT(b+yTR); rgb[4] = LIMIT(g+yTR); rgb[5] = LIMIT(r+yTR); - - /* Skip down to next line to write out bottom two pixels */ - rgb += 3 * rowPixels; - rgb[0] = LIMIT(b+yBL); rgb[1] = LIMIT(g+yBL); rgb[2] = LIMIT(r+yBL); - rgb[3] = LIMIT(b+yBR); rgb[4] = LIMIT(g+yBR); rgb[5] = LIMIT(r+yBR); + if (bits == 24) { + /* Write out top two pixels */ + rgb[0] = LIMIT(b+yTL); rgb[1] = LIMIT(g+yTL); rgb[2] = LIMIT(r+yTL); + rgb[3] = LIMIT(b+yTR); rgb[4] = LIMIT(g+yTR); rgb[5] = LIMIT(r+yTR); + + /* Skip down to next line to write out bottom two pixels */ + rgb += 3 * rowPixels; + rgb[0] = LIMIT(b+yBL); rgb[1] = LIMIT(g+yBL); rgb[2] = LIMIT(r+yBL); + rgb[3] = LIMIT(b+yBR); rgb[4] = LIMIT(g+yBR); rgb[5] = LIMIT(r+yBR); + } else if (bits == 16) { + /* Write out top two pixels */ + rgb[0] = ((LIMIT(b+yTL) >> 3) & 0x1F) | ((LIMIT(g+yTL) << 5) & 0xE0); + rgb[1] = ((LIMIT(g+yTL) >> 5) & 0x07) | (LIMIT(r+yTL) & 0xF8); + rgb[2] = ((LIMIT(b+yTR) >> 3) & 0x1F) | ((LIMIT(g+yTR) << 5) & 0xE0); + rgb[3] = ((LIMIT(g+yTR) >> 5) & 0x07) | (LIMIT(r+yTR) & 0xF8); + + /* Skip down to next line to write out bottom two pixels */ + rgb += 2 * rowPixels; + rgb[0] = ((LIMIT(b+yBL) >> 3) & 0x1F) | ((LIMIT(g+yBL) << 5) & 0xE0); + rgb[1] = ((LIMIT(g+yBL) >> 5) & 0x07) | (LIMIT(r+yBL) & 0xF8); + rgb[2] = ((LIMIT(b+yBR) >> 3) & 0x1F) | ((LIMIT(g+yBR) << 5) & 0xE0); + rgb[3] = ((LIMIT(g+yBR) >> 5) & 0x07) | (LIMIT(r+yBR) & 0xF8); + } } /* @@ -1182,9 +1217,8 @@ /* #define this and OV511_DUMPPIX to disable parsing of UV data */ #undef OV511_FORCE_MONO -#ifdef OV511_GBR422 static void -ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0, +ov511_parse_gbr422_to_rgb24(unsigned char *pIn0, unsigned char *pOut0, int iOutY, int iOutUV, int iHalf, int iWidth) { int k, l, m; @@ -1231,10 +1265,8 @@ } } -#else - static void -ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0, +ov511_parse_yuv420_to_rgb24(unsigned char *pIn0, unsigned char *pOut0, int iOutY, int iOutUV, int iHalf, int iWidth) { #ifndef OV511_DUMPPIX @@ -1271,7 +1303,7 @@ int v = *(pIn+64) - 128; int u = *pIn++ - 128; ov511_move_420_block(y00, y01, y10, y11, u, v, iWidth, - pOut); + pOut, 24); pOut += 6; } pOut += (iWidth*2 - 16) * 3; @@ -1302,7 +1334,7 @@ int v = *pOut1 - 128; int u = *(pOut1+1) - 128; ov511_move_420_block(y00, y01, y10, - y11, u, v, iWidth, pOut1); + y11, u, v, iWidth, pOut1, 24); pOut1 += 6; } pOut1 += (iWidth*2 - 8) * 3; @@ -1367,7 +1399,85 @@ #endif } -#endif + +static void +ov511_parse_yuv420_to_rgb565(unsigned char *pIn0, unsigned char *pOut0, + int iOutY, int iOutUV, int iHalf, int iWidth) +{ + int k, l, m; + unsigned char *pIn; + unsigned char *pOut, *pOut1; + + /* Just copy the Y's if in the first stripe */ + if (!iHalf) { + pIn = pIn0 + 128; + pOut = pOut0 + iOutY; + for (k = 0; k < 4; k++) { + pOut1 = pOut; + for (l = 0; l < 8; l++) { + for (m = 0; m < 8; m++) { + *pOut1 = *pIn++; + pOut1 += 2; + } + pOut1 += (iWidth - 8) * 2; + } + pOut += 8 * 2; + } + } + + /* Use the first half of VUs to calculate value */ + pIn = pIn0; + pOut = pOut0 + iOutUV; + for (l = 0; l < 4; l++) { + for (m=0; m<8; m++) { + int y00 = *(pOut); + int y01 = *(pOut+2); + int y10 = *(pOut+iWidth*2); + int y11 = *(pOut+iWidth*2+2); + int v = *(pIn+64) - 128; + int u = *pIn++ - 128; + ov511_move_420_block(y00, y01, y10, y11, u, v, iWidth, + pOut, 16); + pOut += 4; + } + pOut += (iWidth*2 - 16) * 2; + } + + /* Just copy the other UV rows */ + for (l = 0; l < 4; l++) { + for (m = 0; m < 8; m++) { + *pOut++ = *(pIn + 64); + *pOut = *pIn++; + pOut += 3; + } + pOut += (iWidth*2 - 16) * 2; + } + + /* Calculate values if it's the second half */ + if (iHalf) { + pIn = pIn0 + 128; + pOut = pOut0 + iOutY; + for (k = 0; k < 4; k++) { + pOut1 = pOut; + for (l=0; l<4; l++) { + for (m=0; m<4; m++) { + int y10 = *(pIn+8); + int y00 = *pIn++; + int y11 = *(pIn+8); + int y01 = *pIn++; + int v = *pOut1 - 128; + int u = *(pOut1+1) - 128; + ov511_move_420_block(y00, y01, y10, + y11, u, v, iWidth, pOut1, 16); + pOut1 += 4; + } + pOut1 += (iWidth*2 - 8) * 2; + pIn += 8; + } + pOut += 8 * 2; + } + } +} /* This converts YUV420 segments to YUYV */ static void @@ -1662,6 +1772,11 @@ /* Frame start */ PDEBUG(4, "Frame start, framenum = %d", ov511->curframe); +#if 0 + /* Make sure no previous data carries over; necessary + * for compression experimentation */ + memset(frame->data, 0, MAX_DATA_SIZE); +#endif output_offset = 0; /* Check to see if it's a snapshot frame */ @@ -1740,7 +1855,15 @@ ov511_parse_data_grey (pData, pOut, iOutY, frame->width); break; case VIDEO_PALETTE_RGB24: - ov511_parse_data_rgb24 (pData, pOut, iOutY, iOutUV, + if (sensor_gbr) + ov511_parse_gbr422_to_rgb24(pData, pOut, iOutY, iOutUV, + iY & 1, frame->width); + else + ov511_parse_yuv420_to_rgb24(pData, pOut, iOutY, iOutUV, + iY & 1, frame->width); + break; + case VIDEO_PALETTE_RGB565: + ov511_parse_yuv420_to_rgb565(pData, pOut, iOutY, iOutUV, iY & 1, frame->width); break; case VIDEO_PALETTE_YUV422: @@ -1782,11 +1905,20 @@ static void ov511_isoc_irq(struct urb *urb) { int len; - struct usb_ov511 *ov511 = urb->context; + struct usb_ov511 *ov511; struct ov511_sbuf *sbuf; - if (!ov511->dev) + if (!urb->context) { + PDEBUG(4, "no context"); return; + } + + ov511 = (struct usb_ov511 *) urb->context; + + if (!ov511->dev || !ov511->user) { + PDEBUG(4, "no device, or not open"); + return; + } if (!ov511->streaming) { PDEBUG(4, "hmmm... not streaming, but got interrupt"); @@ -1805,6 +1937,8 @@ /* Move to the next sbuf */ ov511->cursbuf = (ov511->cursbuf + 1) % OV511_NUMSBUF; + urb->dev = ov511->dev; + return; } @@ -1872,6 +2006,7 @@ ov511->sbuf[n].urb->next = ov511->sbuf[n+1].urb; for (n = 0; n < OV511_NUMSBUF; n++) { + ov511->sbuf[n].urb->dev = ov511->dev; err = usb_submit_urb(ov511->sbuf[n].urb); if (err) err("init isoc: usb_submit_urb(%d) ret %d", n, err); @@ -2079,7 +2214,7 @@ static int ov511_open(struct video_device *dev, int flags) { struct usb_ov511 *ov511 = (struct usb_ov511 *)dev; - int err = 0; + int err; MOD_INC_USE_COUNT; PDEBUG(4, "opening"); @@ -2174,8 +2309,8 @@ b.audios = 0; b.maxwidth = ov511->maxwidth; b.maxheight = ov511->maxheight; - b.minwidth = 32; - b.minheight = 16; + b.minwidth = 160; + b.minheight = 120; if (copy_to_user(arg, &b, sizeof(b))) return -EFAULT; @@ -2239,6 +2374,9 @@ if (p.palette != VIDEO_PALETTE_GREY && p.palette != VIDEO_PALETTE_RGB24 && +#ifdef OV511_RGB565 + p.palette != VIDEO_PALETTE_RGB565 && +#endif p.palette != VIDEO_PALETTE_YUV422 && p.palette != VIDEO_PALETTE_YUYV && p.palette != VIDEO_PALETTE_YUV420 && @@ -2383,18 +2521,27 @@ vm.frame, vm.width, vm.height, vm.format); if (vm.format != VIDEO_PALETTE_RGB24 && +#ifdef OV511_RGB565 + vm.format != VIDEO_PALETTE_RGB565 && +#endif vm.format != VIDEO_PALETTE_YUV422 && vm.format != VIDEO_PALETTE_YUYV && vm.format != VIDEO_PALETTE_YUV420 && vm.format != VIDEO_PALETTE_YUV422P && - vm.format != VIDEO_PALETTE_GREY) + vm.format != VIDEO_PALETTE_GREY) { + err("VIDIOCMCAPTURE: invalid format (%d)", vm.format); return -EINVAL; + } - if ((vm.frame != 0) && (vm.frame != 1)) + if ((vm.frame != 0) && (vm.frame != 1)) { + err("VIDIOCMCAPTURE: invalid frame (%d)", vm.frame); return -EINVAL; - - if (vm.width > ov511->maxwidth || vm.height > ov511->maxheight) + } + + if (vm.width > ov511->maxwidth || vm.height > ov511->maxheight) { + err("VIDIOCMCAPTURE: requested dimensions too big"); return -EINVAL; + } if (ov511->frame[vm.frame].grabstate == FRAME_GRABBING) return -EBUSY; @@ -2481,6 +2628,13 @@ goto redo; } case FRAME_DONE: + if (ov511->snap_enabled && !ov511->frame[frame].snapshot) { + int ret; + if ((ret = ov511_new_frame(ov511, frame)) < 0) + return ret; + goto redo; + } + ov511->frame[frame].grabstate = FRAME_UNUSED; /* Reset the hardware snapshot button */ @@ -2689,6 +2843,110 @@ initialize: ov511_init_done, }; +#ifdef OV511_CONTROL_API +/********************************************************************** + * + * Control File Interface + * + * NOTE: This API is definitely subject to change at this time. + * + * Q: Why are you using a device file instead of a proc file? + * A: 1. /proc is for status reporting and not general I/O. + * 2. The proc interface used here won't allow IOCTL. + * + * Q: Why are you using IOCTL instead of read/write? + * A: Passing data in ASCII is a pain (look at cpia's proc support) + * + * Q: Why don't you use the normal video device's IOCTL function? + * A: This would either require that the device be opened multiple + * times, or that every video app implement ov511-control. + * + * I admit this is a shitty hack. I should at elast add a new device + * type to V4L. Hopefully 2.5/V4L2 will obviate the need for this. - MM + * + **********************************************************************/ + +static int ov511_control_open(struct video_device *dev, int flags) +{ + struct usb_ov511 *ov511 = (struct usb_ov511 *)dev; + int err; + + MOD_INC_USE_COUNT; + PDEBUG(4, ""); + down(&ov511->lock); + + err = -EBUSY; + if (ov511->user) + goto out; + + ov511->control_user++; + +out: + up(&ov511->lock); + + if (err) + MOD_DEC_USE_COUNT; + + return err; +} + +static void ov511_control_close(struct video_device *dev) +{ + struct usb_ov511 *ov511 = (struct usb_ov511 *)dev; + + PDEBUG(4, ""); + + down(&ov511->lock); + + ov511->control_user--; + + up(&ov511->lock); + + /* This is definitely broken (as well as the other close()) */ + if (!ov511->dev) { + video_unregister_device(&ov511->vdev); + kfree(ov511); + ov511 = NULL; + } + + MOD_DEC_USE_COUNT; +} + +static int ov511_control_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) +{ + struct usb_ov511 *ov511 = (struct usb_ov511 *)vdev; + + PDEBUG(4, "IOCtl: 0x%X", cmd); + + if (!ov511->dev) + return -EIO; + + switch (cmd) { + /* The purpose of this is to let the control app make sure that it is + * talking to an ov511 (using the name string). All V4L apps that use + * extended IOCTLs should do that! */ + case VIDIOCGCAP: + { + int err; + err = ov511_ioctl(vdev, cmd, arg); + } + default: + return -ENOIOCTLCMD; + } + + return 0; +} + +static struct video_device ov511_control_template = { + name: "OV511 USB Camera (control)", + type: VID_TYPE_CAPTURE, + hardware: VID_HARDWARE_OV511, + open: ov511_control_open, + close: ov511_control_close, + ioctl: ov511_control_ioctl, +}; +#endif + /**************************************************************************** * * OV511/OV7610 configuration @@ -3057,11 +3315,22 @@ init_waitqueue_head(&ov511->wq); - if (video_register_device(&ov511->vdev, VFL_TYPE_GRABBER) == -1) { + if (video_register_device(&ov511->vdev, VFL_TYPE_GRABBER) < 0) { err("video_register_device failed"); return -EBUSY; } +#ifdef OV511_CONTROL_API + /* Create control device. Not a VTX; this is just an awful hack */ + memcpy(&ov511->cdev, &ov511_control_template, + sizeof(ov511_control_template)); + if (video_register_device(&ov511->cdev, VFL_TYPE_VTX) < 0) { + err("video_register_device failed for control device"); + video_unregister_device(&ov511->vdev); + return -EBUSY; + } +#endif + if (ov511_write_regvals(dev, aRegvalsInit)) goto error; if (ov511_write_regvals(dev, aRegvalsNorm511)) goto error; @@ -3131,6 +3400,9 @@ error: video_unregister_device(&ov511->vdev); +#ifdef OV511_CONTROL_API + video_unregister_device(&ov511->cdev); +#endif usb_driver_release_interface(&ov511_driver, &dev->actconfig->interface[ov511->iface]); @@ -3159,16 +3431,17 @@ interface = &dev->actconfig->interface[ifnum].altsetting[0]; /* Is it an OV511/OV511+? */ - if (dev->descriptor.idVendor != 0x05a9 - && dev->descriptor.idVendor != 0x0813) + if (dev->descriptor.idVendor == 0x05a9 + && dev->descriptor.idProduct != 0x0511 + && dev->descriptor.idProduct != 0xA511) return NULL; - if (dev->descriptor.idProduct != 0x0511 - && dev->descriptor.idProduct != 0xA511 + + if (dev->descriptor.idVendor == 0x0813 && dev->descriptor.idProduct != 0x0002) return NULL; /* Checking vendor/product should be enough, but what the hell */ - if (interface->bInterfaceClass != 0xFF) + if (interface->bInterfaceClass != 0xFF) return NULL; if (interface->bInterfaceSubClass != 0x00) return NULL; @@ -3277,6 +3550,13 @@ video_unregister_device(&ov511->vdev); else PDEBUG(3, "Device open...deferring video_unregister_device"); + +#ifdef OV511_CONTROL_API + if (!ov511->control_user) + video_unregister_device(&ov511->cdev); + else + PDEBUG(3, "Control device open...deferring video_unregister_device"); +#endif for (n = 0; n < OV511_NUMFRAMES; n++) ov511->frame[n].grabstate = FRAME_ERROR;