diff -Naur linux-2.4.0-test3-pre6-orig/Documentation/usb/ov511.txt linux-2.4.0-test3-pre6/Documentation/usb/ov511.txt --- linux-2.4.0-test3-pre6-orig/Documentation/usb/ov511.txt Sun Jun 25 18:57:18 2000 +++ linux-2.4.0-test3-pre6/Documentation/usb/ov511.txt Fri Jul 7 22:38:40 2000 @@ -6,7 +6,9 @@ Homepage: http://alpha.dyndns.org/ov511 NEW IN THIS VERSION: - o Race conditions and other bugs fixed + o Sensor detection fixes + o More efficient/reliable buffer allocation + o Many minor fixes INTRODUCTION: @@ -176,7 +178,7 @@ WORKING FEATURES: o Color streaming/capture at 640x480, 448x336, 384x288, 352x288, and 320x240 - o YUV420 color + o YUV420 and YUV422P color o Monochrome o Setting/getting of saturation, contrast and brightness (no hue yet; only works with OV7610, not the OV7620 or OV7620AE) @@ -209,7 +211,7 @@ o Setting of contrast and brightness not working with 7620 o Driver/camera state save/restore for when USB supports suspend/resume o Multiple cameras reportedly do not work simultaneously - o Problems with OHCI + o Unstable on SMP systems HOW TO CONTACT ME: diff -Naur linux-2.4.0-test3-pre6-orig/drivers/usb/ov511.c linux-2.4.0-test3-pre6/drivers/usb/ov511.c --- linux-2.4.0-test3-pre6-orig/drivers/usb/ov511.c Sun Jun 25 18:58:13 2000 +++ linux-2.4.0-test3-pre6/drivers/usb/ov511.c Fri Jul 7 22:06:09 2000 @@ -30,7 +30,7 @@ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -static const char version[] = "1.17"; +static const char version[] = "1.19"; #define __NO_VERSION__ @@ -49,10 +49,6 @@ #include #include -#ifdef CONFIG_KMOD -#include -#endif - #include "ov511.h" #undef OV511_GBR422 /* Experimental -- sets the 7610 to GBR422 */ @@ -67,7 +63,7 @@ #define DEFAULT_HEIGHT 480 #define GET_SEGSIZE(p) ((p) == VIDEO_PALETTE_GREY ? 256 : 384) -#define GET_DEPTH(p) ((p) == VIDEO_PALETTE_GREY ? 8 : 24) +#define GET_DEPTH(p) ((p) == VIDEO_PALETTE_GREY ? 8 : ((p) == VIDEO_PALETTE_YUV422 ? 8 : 24)) /* PARAMETER VARIABLES: */ static int autoadjust = 1; /* CCD dynamically changes exposure, etc... */ @@ -102,6 +98,9 @@ * programs that expect RGB data (e.g. gqcam) to work with this driver. */ static int force_rgb = 0; +/* Number of seconds before inactive buffers are deallocated */ +static int buf_timeout = 5; + MODULE_PARM(autoadjust, "i"); MODULE_PARM(debug, "i"); MODULE_PARM(fix_rgb_offset, "i"); @@ -110,6 +109,7 @@ MODULE_PARM(i2c_detect_tries, "i"); MODULE_PARM(aperture, "i"); MODULE_PARM(force_rgb, "i"); +MODULE_PARM(buf_timeout, "i"); MODULE_AUTHOR("Mark McClelland & Bret Wallach & Orion Sky Lawlor & Kevin Moore & Charl P. Botha & Claudio Matsuoka "); MODULE_DESCRIPTION("OV511 USB Camera Driver"); @@ -136,6 +136,7 @@ { -1, NULL } }; +#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) static struct palette_list plist[] = { { VIDEO_PALETTE_GREY, "GREY" }, { VIDEO_PALETTE_HI240, "HI240" }, @@ -155,6 +156,7 @@ { VIDEO_PALETTE_YUV410P,"YUV410P" }, { -1, NULL } }; +#endif /********************************************************************** * @@ -436,7 +438,7 @@ PDEBUG(5, "reg write: 0x%02X:0x%02X, 0x%x", reg, value, rc); if (rc < 0) - err("ov511_reg_write: error %d", rc); + err("reg write: error %d", rc); return rc; } @@ -456,7 +458,7 @@ PDEBUG(5, "reg read: 0x%02X:0x%02X", reg, buffer[0]); if (rc < 0) { - err("ov511_reg_read: error %d", rc); + err("reg read: error %d", rc); return rc; } else { return buffer[0]; @@ -505,7 +507,7 @@ return 0; error: - err("ov511_i2c_write: error %d", rc); + err("i2c write: error %d", rc); return rc; } @@ -577,7 +579,7 @@ return value; error: - err("ov511_i2c_read: error %d", rc); + err("i2c read: error %d", rc); return rc; } @@ -605,7 +607,7 @@ return 0; error: - err("ov511_write_regvals: error %d", rc); + err("write regvals: error %d", rc); return rc; } @@ -626,6 +628,7 @@ ov511_dump_i2c_range(dev, 0x00, 0x38); } +#if 0 static void ov511_dump_reg_range(struct usb_device *dev, int reg1, int regn) { int i; @@ -636,7 +639,6 @@ } } -#if 0 static void ov511_dump_regs(struct usb_device *dev) { PDEBUG(1, "CAMERA INTERFACE REGS"); @@ -703,11 +705,8 @@ if (ov511->bridge == BRG_OV511) { if (size == 0) alt = OV511_ALT_SIZE_0; else if (size == 257) alt = OV511_ALT_SIZE_257; -// else if (size == 512) alt = OV511_ALT_SIZE_512; else if (size == 513) alt = OV511_ALT_SIZE_513; -// else if (size == 768) alt = OV511_ALT_SIZE_768; else if (size == 769) alt = OV511_ALT_SIZE_769; -// else if (size == 992) alt = OV511_ALT_SIZE_992; else if (size == 993) alt = OV511_ALT_SIZE_993; else { err("Set packet size: invalid size (%d)", size); @@ -1088,7 +1087,7 @@ #ifdef OV511_GBR422 static void ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0, - int iOutY, int iOutUV, int iHalf, int iWidth) + int iOutY, int iOutUV, int iHalf, int iWidth) { int k, l, m; unsigned char *pIn; @@ -1138,7 +1137,7 @@ static void ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0, - int iOutY, int iOutUV, int iHalf, int iWidth) + int iOutY, int iOutUV, int iHalf, int iWidth) { #ifndef OV511_DUMPPIX int k, l, m; @@ -1231,6 +1230,139 @@ } #endif +static void +ov511_parse_data_yuv422(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++) & 0xF0; + } + pOut1 += iWidth - 8; + } + pOut += 8; + } + } + + /* 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++) { + unsigned char *p00 = pOut; + unsigned char *p01 = pOut+1; + unsigned char *p10 = pOut+iWidth; + unsigned char *p11 = pOut+iWidth+1; + int v = *(pIn+64) - 128; + int u = *pIn++ - 128; + int uv = ((u >> 4) & 0x0C) + (v >> 6); + + *p00 |= uv; + *p01 |= uv; + *p10 |= uv; + *p11 |= uv; + + pOut += 2; + } + pOut += (iWidth*2 - 16); + } + + /* Just copy the other UV rows */ + for (l = 0; l < 4; l++) { + for (m = 0; m < 8; m++) { + int v = *(pIn + 64) - 128; + int u = (*pIn++) - 128; + int uv = ((u >> 4) & 0x0C) + (v >> 6); + *(pOut) = uv; + pOut += 2; + } + pOut += (iWidth*2 - 16); + } + + /* 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 uv = *pOut1; + + *pOut1 = (y00 & 0xF0) | uv; + *(pOut1+1) = (y01 & 0xF0) | uv; + *(pOut1+iWidth) = (y10 & 0xF0) | uv; + *(pOut1+iWidth+1) = (y11 & 0xF0) | uv; + + pOut1 += 2; + } + pOut1 += (iWidth*2 - 8); + pIn += 8; + } + pOut += 8; + } + } +} + +static void +ov511_parse_data_yuv422p(unsigned char *pIn0, unsigned char *pOut0, + int iOutY, int iOutUV, int iWidth, int iHeight) +{ + int k, l, m; + unsigned char *pIn; + unsigned char *pOut, *pOut1; + unsigned a = iWidth * iHeight; + unsigned w = iWidth / 2; + + pIn = pIn0; + pOut = pOut0 + iOutUV + a; + for (k = 0; k < 8; k++) { + pOut1 = pOut; + for (l = 0; l < 8; l++) { + *pOut1 = *(pOut1 + w) = *pIn++; + pOut1++; + } + pOut += iWidth; + } + + pIn = pIn0 + 64; + pOut = pOut0 + iOutUV + a + a/2; + for (k = 0; k < 8; k++) { + pOut1 = pOut; + for (l = 0; l < 8; l++) { + *pOut1 = *(pOut1 + w) = *pIn++; + pOut1++; + } + pOut += iWidth; + } + + 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 += iWidth - 8; + } + pOut += 8; + } +} + /* * For 640x480 RAW BW images, data shows up in 1200 256 byte segments. * The segments represent 4 squares of 8x8 pixels as follows: @@ -1432,7 +1564,7 @@ frame->segment < frame->width * frame->height / 256) { int iSegY, iSegUV; int iY, jY, iUV, jUV; - int iOutY, iOutUV; + int iOutY, iOutYP, iOutUV, iOutUVP; unsigned char *pOut; iSegY = iSegUV = frame->segment; @@ -1465,10 +1597,12 @@ */ iY = iSegY / (frame->width / WDIV); jY = iSegY - iY * (frame->width / WDIV); - iOutY = (iY*HDIV*frame->width + jY*WDIV) * (frame->depth >> 3); + iOutYP = iY*HDIV*frame->width + jY*WDIV; + iOutY = iOutYP * (frame->depth >> 3); iUV = iSegUV / (frame->width / WDIV * 2); jUV = iSegUV - iUV * (frame->width / WDIV * 2); - iOutUV = (iUV*HDIV*2*frame->width + jUV*WDIV/2) * (frame->depth >> 3); + iOutUVP = iUV*HDIV*2*frame->width + jUV*WDIV/2; + iOutUV = iOutUVP * (frame->depth >> 3); switch (frame->format) { case VIDEO_PALETTE_GREY: @@ -1478,6 +1612,16 @@ ov511_parse_data_rgb24 (pData, pOut, iOutY, iOutUV, iY & 1, frame->width); break; + case VIDEO_PALETTE_YUV422: + ov511_parse_data_yuv422(pData, pOut, iOutY, iOutUV, + iY & 1, frame->width); + break; + case VIDEO_PALETTE_YUV422P: + ov511_parse_data_yuv422p (pData, pOut, iOutYP, iOutUVP/2, + frame->width, frame->height); + break; + default: + err("Unsupported format: %d", frame->format); } pData = &cdata[iPix]; @@ -1532,7 +1676,7 @@ static int ov511_init_isoc(struct usb_ov511 *ov511) { urb_t *urb; - int fx, err; + int fx, err, n; PDEBUG(3, "*** Initializing capture ***"); @@ -1548,55 +1692,37 @@ else err("invalid bridge type"); - /* We double buffer the Iso lists */ - urb = usb_alloc_urb(FRAMES_PER_DESC); + for (n = 0; n < OV511_NUMSBUF; n++) { + urb = usb_alloc_urb(FRAMES_PER_DESC); - if (!urb) { - err("ov511_init_isoc: usb_alloc_urb ret. NULL"); - return -ENOMEM; - } - ov511->sbuf[0].urb = urb; - urb->dev = ov511->dev; - urb->context = ov511; - urb->pipe = usb_rcvisocpipe(ov511->dev, OV511_ENDPOINT_ADDRESS); - urb->transfer_flags = USB_ISO_ASAP; - urb->transfer_buffer = ov511->sbuf[0].data; - urb->complete = ov511_isoc_irq; - urb->number_of_packets = FRAMES_PER_DESC; - urb->transfer_buffer_length = ov511->packet_size * FRAMES_PER_DESC; - for (fx = 0; fx < FRAMES_PER_DESC; fx++) { - urb->iso_frame_desc[fx].offset = ov511->packet_size * fx; - urb->iso_frame_desc[fx].length = ov511->packet_size; - } - - urb = usb_alloc_urb(FRAMES_PER_DESC); - if (!urb) { - err("ov511_init_isoc: usb_alloc_urb ret. NULL"); - return -ENOMEM; - } - ov511->sbuf[1].urb = urb; - urb->dev = ov511->dev; - urb->context = ov511; - urb->pipe = usb_rcvisocpipe(ov511->dev, OV511_ENDPOINT_ADDRESS); - urb->transfer_flags = USB_ISO_ASAP; - urb->transfer_buffer = ov511->sbuf[1].data; - urb->complete = ov511_isoc_irq; - urb->number_of_packets = FRAMES_PER_DESC; - urb->transfer_buffer_length = ov511->packet_size * FRAMES_PER_DESC; - for (fx = 0; fx < FRAMES_PER_DESC; fx++) { - urb->iso_frame_desc[fx].offset = ov511->packet_size * fx; - urb->iso_frame_desc[fx].length = ov511->packet_size; + if (!urb) { + err("init isoc: usb_alloc_urb ret. NULL"); + return -ENOMEM; + } + ov511->sbuf[n].urb = urb; + urb->dev = ov511->dev; + urb->context = ov511; + urb->pipe = usb_rcvisocpipe(ov511->dev, OV511_ENDPOINT_ADDRESS); + urb->transfer_flags = USB_ISO_ASAP; + urb->transfer_buffer = ov511->sbuf[n].data; + urb->complete = ov511_isoc_irq; + urb->number_of_packets = FRAMES_PER_DESC; + urb->transfer_buffer_length = ov511->packet_size * FRAMES_PER_DESC; + for (fx = 0; fx < FRAMES_PER_DESC; fx++) { + urb->iso_frame_desc[fx].offset = ov511->packet_size * fx; + urb->iso_frame_desc[fx].length = ov511->packet_size; + } } - ov511->sbuf[1].urb->next = ov511->sbuf[0].urb; - ov511->sbuf[0].urb->next = ov511->sbuf[1].urb; - - err = usb_submit_urb(ov511->sbuf[0].urb); - if (err) - err("ov511_init_isoc: usb_submit_urb(0) ret %d", err); - err = usb_submit_urb(ov511->sbuf[1].urb); - if (err) - err("ov511_init_isoc: usb_submit_urb(1) ret %d", err); + ov511->sbuf[OV511_NUMSBUF - 1].urb->next = ov511->sbuf[0].urb; + for (n = 0; n < OV511_NUMSBUF - 1; n++) + ov511->sbuf[n].urb->next = ov511->sbuf[n+1].urb; + + for (n = 0; n < OV511_NUMSBUF; n++) { + err = usb_submit_urb(ov511->sbuf[n].urb); + if (err) + err("init isoc: usb_submit_urb(%d) ret %d", n, err); + } ov511->streaming = 1; @@ -1605,6 +1731,8 @@ static void ov511_stop_isoc(struct usb_ov511 *ov511) { + int n; + if (!ov511->streaming || !ov511->dev) return; @@ -1615,17 +1743,13 @@ ov511->streaming = 0; /* Unschedule all of the iso td's */ - if (ov511->sbuf[1].urb) { - ov511->sbuf[1].urb->next = NULL; - usb_unlink_urb(ov511->sbuf[1].urb); - usb_free_urb(ov511->sbuf[1].urb); - ov511->sbuf[1].urb = NULL; - } - if (ov511->sbuf[0].urb) { - ov511->sbuf[0].urb->next = NULL; - usb_unlink_urb(ov511->sbuf[0].urb); - usb_free_urb(ov511->sbuf[0].urb); - ov511->sbuf[0].urb = NULL; + for (n = OV511_NUMSBUF - 1; n >= 0; n--) { + if (ov511->sbuf[n].urb) { + ov511->sbuf[n].urb->next = NULL; + usb_unlink_urb(ov511->sbuf[n].urb); + usb_free_urb(ov511->sbuf[n].urb); + ov511->sbuf[n].urb = NULL; + } } } @@ -1677,6 +1801,127 @@ /**************************************************************************** * + * Buffer management + * + ***************************************************************************/ +static int ov511_alloc(struct usb_ov511 *ov511) +{ + int i; + + PDEBUG(4, "entered"); + down(&ov511->buf_lock); + + if (ov511->buf_state == BUF_PEND_DEALLOC) { + ov511->buf_state = BUF_ALLOCATED; + del_timer(&ov511->buf_timer); + } + + if (ov511->buf_state == BUF_ALLOCATED) + goto out; + + ov511->fbuf = rvmalloc(OV511_NUMFRAMES * MAX_DATA_SIZE); + if (!ov511->fbuf) + goto error; + + for (i = 0; i < OV511_NUMFRAMES; i++) { + ov511->frame[i].grabstate = FRAME_UNUSED; + ov511->frame[i].data = ov511->fbuf + i * MAX_DATA_SIZE; + PDEBUG(4, "frame[%d] @ %p", i, ov511->frame[i].data); + + ov511->sbuf[i].data = kmalloc(FRAMES_PER_DESC * + MAX_FRAME_SIZE_PER_DESC, GFP_KERNEL); + if (!ov511->sbuf[i].data) { + while (--i) { + kfree(ov511->sbuf[i].data); + ov511->sbuf[i].data = NULL; + } + rvfree(ov511->fbuf, OV511_NUMFRAMES * MAX_DATA_SIZE); + ov511->fbuf = NULL; + goto error; + } + PDEBUG(4, "sbuf[%d] @ %p", i, ov511->sbuf[i].data); + } + ov511->buf_state = BUF_ALLOCATED; +out: + up(&ov511->buf_lock); + PDEBUG(4, "leaving"); + return 0; +error: + ov511->buf_state = BUF_NOT_ALLOCATED; + up(&ov511->buf_lock); + PDEBUG(4, "errored"); + return -ENOMEM; +} + +/* + * - You must acquire buf_lock before entering this function. + * - Because this code will free any non-null pointer, you must be sure to null + * them if you explicitly free them somewhere else! + */ +static void ov511_do_dealloc(struct usb_ov511 *ov511) +{ + int i; + PDEBUG(4, "entered"); + + if (ov511->fbuf) { + rvfree(ov511->fbuf, OV511_NUMFRAMES * MAX_DATA_SIZE); + ov511->fbuf = NULL; + } + + for (i = 0; i < OV511_NUMFRAMES; i++) { + if (ov511->sbuf[i].data) { + kfree(ov511->sbuf[i].data); + ov511->sbuf[i].data = NULL; + } + } + + PDEBUG(4, "buffer memory deallocated"); + ov511->buf_state = BUF_NOT_ALLOCATED; + PDEBUG(4, "leaving"); +} + +static void ov511_buf_callback(unsigned long data) +{ + struct usb_ov511 *ov511 = (struct usb_ov511 *)data; + PDEBUG(4, "entered"); + down(&ov511->buf_lock); + + if (ov511->buf_state == BUF_PEND_DEALLOC) + ov511_do_dealloc(ov511); + + up(&ov511->buf_lock); + PDEBUG(4, "leaving"); +} + +static void ov511_dealloc(struct usb_ov511 *ov511, int now) +{ + struct timer_list *bt = &(ov511->buf_timer); + PDEBUG(4, "entered"); + down(&ov511->buf_lock); + + PDEBUG(4, "deallocating buffer memory %s", now ? "now" : "later"); + + if (ov511->buf_state == BUF_PEND_DEALLOC) { + ov511->buf_state = BUF_ALLOCATED; + del_timer(bt); + } + + if (now) + ov511_do_dealloc(ov511); + else { + ov511->buf_state = BUF_PEND_DEALLOC; + init_timer(bt); + bt->function = ov511_buf_callback; + bt->data = (unsigned long)ov511; + bt->expires = jiffies + buf_timeout * HZ; + add_timer(bt); + } + up(&ov511->buf_lock); + PDEBUG(4, "leaving"); +} + +/**************************************************************************** + * * V4L API * ***************************************************************************/ @@ -1684,7 +1929,7 @@ static int ov511_open(struct video_device *dev, int flags) { struct usb_ov511 *ov511 = (struct usb_ov511 *)dev; - int i, err = 0; + int err = 0; MOD_INC_USE_COUNT; PDEBUG(4, "opening"); @@ -1695,33 +1940,16 @@ goto out; err = -ENOMEM; - - /* Allocate memory for the frame buffers */ - ov511->fbuf = rvmalloc(OV511_NUMFRAMES * MAX_DATA_SIZE); - if (!ov511->fbuf) + if (ov511_alloc(ov511)) goto out; ov511->sub_flag = 0; - for (i = 0; i < OV511_NUMFRAMES; i++) { - ov511->frame[i].grabstate = FRAME_UNUSED; - ov511->frame[i].data = ov511->fbuf + i * MAX_DATA_SIZE; - PDEBUG(4, "frame [%d] @ %p", i, ov511->frame[0].data); - - ov511->sbuf[i].data = kmalloc(FRAMES_PER_DESC * - MAX_FRAME_SIZE_PER_DESC, GFP_KERNEL); - if (!ov511->sbuf[i].data) { -open_free_ret: - while (--i) kfree(ov511->sbuf[i].data); - rvfree(ov511->fbuf, 2 * MAX_DATA_SIZE); - goto out; - } - PDEBUG(4, "sbuf[%d] @ %p", i, ov511->sbuf[i].data); - } - err = ov511_init_isoc(ov511); - if (err) - goto open_free_ret; + if (err) { + ov511_dealloc(ov511, 0); + goto out; + } ov511->user++; @@ -1737,7 +1965,6 @@ static void ov511_close(struct video_device *dev) { struct usb_ov511 *ov511 = (struct usb_ov511 *)dev; - int i; PDEBUG(4, "ov511_close"); @@ -1746,15 +1973,13 @@ ov511_stop_isoc(ov511); - rvfree(ov511->fbuf, OV511_NUMFRAMES * MAX_DATA_SIZE); - for (i = 0; i < OV511_NUMFRAMES; i++) - kfree(ov511->sbuf[i].data); - + ov511_dealloc(ov511, 0); up(&ov511->lock); if (!ov511->dev) { video_unregister_device(&ov511->vdev); kfree(ov511); + ov511 = NULL; } MOD_DEC_USE_COUNT; @@ -1977,8 +2202,8 @@ struct video_mbuf vm; memset(&vm, 0, sizeof(vm)); - vm.size = 2 * MAX_DATA_SIZE; - vm.frames = 2; + vm.size = OV511_NUMFRAMES * MAX_DATA_SIZE; + vm.frames = OV511_NUMFRAMES; vm.offsets[0] = 0; vm.offsets[1] = MAX_FRAME_SIZE + sizeof (struct timeval); @@ -1995,17 +2220,22 @@ if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm))) return -EFAULT; - PDEBUG(4, "MCAPTURE"); + PDEBUG(4, "CMCAPTURE"); PDEBUG(4, "frame: %d, size: %dx%d, format: %d", vm.frame, vm.width, vm.height, vm.format); if (vm.format != VIDEO_PALETTE_RGB24 && + vm.format != VIDEO_PALETTE_YUV422 && + vm.format != VIDEO_PALETTE_YUV422P && vm.format != VIDEO_PALETTE_GREY) return -EINVAL; if ((vm.frame != 0) && (vm.frame != 1)) return -EINVAL; + if (vm.width > DEFAULT_WIDTH || vm.height > DEFAULT_HEIGHT) + return -EINVAL; + if (ov511->frame[vm.frame].grabstate == FRAME_GRABBING) return -EBUSY; @@ -2183,7 +2413,7 @@ frame->bytes_read = 0; err("** ick! ** Errored frame %d", ov511->curframe); if (ov511_new_frame(ov511, frmx)) - err("ov511_read: ov511_new_frame error"); + err("read: ov511_new_frame error"); goto restart; } @@ -2385,15 +2615,24 @@ /* Reset the 76xx */ if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1; +#if 1 /* Maybe this will fix detection problems? MM */ + /* Wait for it to initialize */ + schedule_timeout (1 + 150 * HZ / 1000); +#endif + for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) { if ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) == 0x7F) && - (ov511_i2c_read(dev, OV7610_REG_ID_LOW) == 0xA2)) + (ov511_i2c_read(dev, OV7610_REG_ID_LOW) == 0xA2)) { success = 1; + continue; + } - /* Dummy read to sync I2C */ - if (ov511_i2c_read(dev, 0x00) < 0) return -1; + /* Reset the 76xx */ + if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1; /* Wait for it to initialize */ schedule_timeout (1 + 150 * HZ / 1000); + /* Dummy read to sync I2C */ + if (ov511_i2c_read(dev, 0x00) < 0) return -1; } if (success) { @@ -2501,8 +2740,9 @@ memcpy(&ov511->vdev, &ov511_template, sizeof(ov511_template)); - init_waitqueue_head(&ov511->frame[0].wq); - init_waitqueue_head(&ov511->frame[1].wq); + for (i = 0; i < OV511_NUMFRAMES; i++) + init_waitqueue_head(&ov511->frame[i].wq); + init_waitqueue_head(&ov511->wq); if (video_register_device(&ov511->vdev, VFL_TYPE_GRABBER) == -1) { @@ -2644,6 +2884,8 @@ if (!ov511_configure(ov511)) { ov511->user = 0; init_MUTEX(&ov511->lock); /* to 1 == available */ + init_MUTEX(&ov511->buf_lock); + ov511->buf_state = BUF_NOT_ALLOCATED; } else { err("Failed to configure camera"); goto error; @@ -2666,6 +2908,7 @@ static void ov511_disconnect(struct usb_device *dev, void *ptr) { struct usb_ov511 *ov511 = (struct usb_ov511 *) ptr; + int n; MOD_INC_USE_COUNT; @@ -2677,38 +2920,37 @@ &ov511->dev->actconfig->interface[ov511->iface]); ov511->dev = NULL; - ov511->frame[0].grabstate = FRAME_ERROR; - ov511->frame[1].grabstate = FRAME_ERROR; + for (n = 0; n < OV511_NUMFRAMES; n++) + ov511->frame[n].grabstate = FRAME_ERROR; + ov511->curframe = -1; /* This will cause the process to request another frame */ - if (waitqueue_active(&ov511->frame[0].wq)) - wake_up_interruptible(&ov511->frame[0].wq); - if (waitqueue_active(&ov511->frame[1].wq)) - wake_up_interruptible(&ov511->frame[1].wq); + for (n = 0; n < OV511_NUMFRAMES; n++) + if (waitqueue_active(&ov511->frame[n].wq)) + wake_up_interruptible(&ov511->frame[n].wq); if (waitqueue_active(&ov511->wq)) wake_up_interruptible(&ov511->wq); ov511->streaming = 0; /* Unschedule all of the iso td's */ - if (ov511->sbuf[1].urb) { - ov511->sbuf[1].urb->next = NULL; - usb_unlink_urb(ov511->sbuf[1].urb); - usb_free_urb(ov511->sbuf[1].urb); - ov511->sbuf[1].urb = NULL; - } - if (ov511->sbuf[0].urb) { - ov511->sbuf[0].urb->next = NULL; - usb_unlink_urb(ov511->sbuf[0].urb); - usb_free_urb(ov511->sbuf[0].urb); - ov511->sbuf[0].urb = NULL; + for (n = OV511_NUMSBUF - 1; n >= 0; n--) { + if (ov511->sbuf[n].urb) { + ov511->sbuf[n].urb->next = NULL; + usb_unlink_urb(ov511->sbuf[n].urb); + usb_free_urb(ov511->sbuf[n].urb); + ov511->sbuf[n].urb = NULL; + } } #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) destroy_proc_ov511_cam(ov511); #endif + /* FIXME - is this correct/safe? Should we acquire ov511->lock? */ + ov511_dealloc(ov511, 1); + /* Free the memory */ if (ov511 && !ov511->user) { kfree(ov511); @@ -2749,7 +2991,7 @@ static void __exit usb_ov511_exit(void) { usb_deregister(&ov511_driver); - info("ov511 driver deregistered"); + info("driver deregistered"); #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) proc_ov511_destroy(); diff -Naur linux-2.4.0-test3-pre6-orig/drivers/usb/ov511.h linux-2.4.0-test3-pre6/drivers/usb/ov511.h --- linux-2.4.0-test3-pre6-orig/drivers/usb/ov511.h Sun Jun 25 18:58:13 2000 +++ linux-2.4.0-test3-pre6/drivers/usb/ov511.h Fri Jul 7 22:06:13 2000 @@ -171,9 +171,7 @@ #define OV7610_REG_COM_K 0x38 /* misc registers */ -#define STREAM_BUF_SIZE (PAGE_SIZE * 4) - -#define SCRATCH_BUF_SIZE 384 +#define SCRATCH_BUF_SIZE 512 #define FRAMES_PER_DESC 10 /* FIXME - What should this be? */ #define FRAME_SIZE_PER_DESC 993 /* FIXME - Deprecated */ @@ -213,6 +211,13 @@ STATE_LINES, /* Parsing lines */ }; +/* Buffer states */ +enum { + BUF_NOT_ALLOCATED, + BUF_ALLOCATED, + BUF_PEND_DEALLOC, /* ov511->buf_timer is set */ +}; + struct usb_device; struct ov511_sbuf { @@ -324,6 +329,11 @@ /* proc interface */ struct semaphore param_lock; /* params lock for this camera */ struct proc_dir_entry *proc_entry; /* /proc/ov511/videoX */ + + /* Framebuffer/sbuf management */ + int buf_state; + struct semaphore buf_lock; + struct timer_list buf_timer; }; struct cam_list {