ChangeSet 1.1019.1.25, 2003/07/30 14:11:03-07:00, nemosoft@smcc.demon.nl [PATCH] USB: PWC 8.11 Attached are two patches, one for 2.4.21 and 2.5.75 for the PWC driver. I assume the 2.5.75 patch will go into 2.6.0-test* without problems (I hope this driver can make it into the kernel before the 'real' 2.6.0). From the ChangeLog: * 20 dev_hints (per request) * Hot unplugging should be better, no more dangling pointers or memory leaks * Added reserved Logitech webcam IDs * Device now remembers size & fps between close()/open() * Removed palette stuff altogether I have two open issues, though: Oliver Neukem pointed out that I should resubmit URBs in the 2.5. kernel even in case of USB errors, which I did. However, I never got a patch so I'm not 100% if this is the solution that he had in mind. Second... I've been thinking long and hard about the problem of properly deregistering the video device when the cam gets unplugged while it is in use. Various schemes failed; immediately deregistering while in the disconnect routine causes crashes because the videodev layer sets some pointer to null but still uses it later. A deregister in close() causes hangs because of locked mutexes... My current implemententation is to set an errorflag in the disconnect routine, then wait there (using schedule()) until close() is being called (I assume the application will immediately close the device when it gets a serious error). So far it doesn't crash :-) drivers/usb/pwc-ctrl.c | 41 ---- drivers/usb/pwc-if.c | 405 +++++++++++++++++++------------------------ drivers/usb/pwc-ioctl.h | 2 drivers/usb/pwc-misc.c | 4 drivers/usb/pwc-uncompress.c | 19 -- drivers/usb/pwc-uncompress.h | 2 drivers/usb/pwc.h | 27 +- 7 files changed, 210 insertions(+), 290 deletions(-) diff -Nru a/drivers/usb/pwc-ctrl.c b/drivers/usb/pwc-ctrl.c --- a/drivers/usb/pwc-ctrl.c Thu Aug 28 14:50:23 2003 +++ b/drivers/usb/pwc-ctrl.c Thu Aug 28 14:50:23 2003 @@ -1,7 +1,7 @@ /* Driver for Philips webcam Functions that send various control messages to the webcam, including video modes. - (C) 1999-2002 Nemosoft Unv. (webcam@smcc.demon.nl) + (C) 1999-2003 Nemosoft Unv. (webcam@smcc.demon.nl) 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 @@ -452,7 +452,7 @@ pdev->view.x = width; pdev->view.y = height; pwc_set_image_buffer_size(pdev); - Trace(TRACE_SIZE, "Set viewport to %dx%d, image size is %dx%d, palette = %d.\n", width, height, pwc_image_sizes[size].x, pwc_image_sizes[size].y, pdev->vpalette); + Trace(TRACE_SIZE, "Set viewport to %dx%d, image size is %dx%d.\n", width, height, pwc_image_sizes[size].x, pwc_image_sizes[size].y); return 0; } @@ -461,38 +461,9 @@ { int factor, i, filler = 0; - switch(pdev->vpalette) { - case VIDEO_PALETTE_RGB32 | 0x80: - case VIDEO_PALETTE_RGB32: - factor = 16; - filler = 0; - break; - case VIDEO_PALETTE_RGB24 | 0x80: - case VIDEO_PALETTE_RGB24: - factor = 12; - filler = 0; - break; - case VIDEO_PALETTE_YUYV: - case VIDEO_PALETTE_YUV422: - factor = 8; - filler = 128; - break; - case VIDEO_PALETTE_YUV420: - case VIDEO_PALETTE_YUV420P: - factor = 6; - filler = 128; - break; -#if PWC_DEBUG - case VIDEO_PALETTE_RAW: - pdev->image.size = pdev->frame_size; - pdev->view.size = pdev->frame_size; - return; - break; -#endif - default: - factor = 0; - break; - } + /* for PALETTE_YUV420P */ + factor = 6; + filler = 128; /* Set sizes in bytes */ pdev->image.size = pdev->image.x * pdev->image.y * factor / 4; @@ -1358,7 +1329,7 @@ { struct pwc_probe probe; - strcpy(probe.name, pdev->vdev->name); + strcpy(probe.name, pdev->vdev.name); probe.type = pdev->type; if (copy_to_user(arg, &probe, sizeof(probe))) ret = -EFAULT; diff -Nru a/drivers/usb/pwc-if.c b/drivers/usb/pwc-if.c --- a/drivers/usb/pwc-if.c Thu Aug 28 14:50:23 2003 +++ b/drivers/usb/pwc-if.c Thu Aug 28 14:50:23 2003 @@ -1,6 +1,6 @@ -/* Linux driver for Philips webcam +/* Linux driver for Philips webcam USB and Video4Linux interface part. - (C) 1999-2002 Nemosoft Unv. + (C) 1999-2003 Nemosoft Unv. 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 @@ -75,14 +75,21 @@ { USB_DEVICE(0x0471, 0x0310) }, { USB_DEVICE(0x0471, 0x0311) }, { USB_DEVICE(0x0471, 0x0312) }, + { USB_DEVICE(0x0471, 0x0313) }, /* the 'new' 720K */ { USB_DEVICE(0x069A, 0x0001) }, /* Askey */ - { USB_DEVICE(0x046D, 0x08b0) }, /* Logitech QuickCam Pro 3000 */ - { USB_DEVICE(0x046D, 0x08b1) }, /* Logitech QuickCam Notebook Pro */ - { USB_DEVICE(0x046d, 0x08b2) }, /* Logitech QuickCam Pro 4000 */ - { USB_DEVICE(0x046d, 0x08b3) }, /* Logitech QuickCam Zoom */ + { USB_DEVICE(0x046D, 0x08B0) }, /* Logitech QuickCam Pro 3000 */ + { USB_DEVICE(0x046D, 0x08B1) }, /* Logitech QuickCam Notebook Pro */ + { USB_DEVICE(0x046D, 0x08B2) }, /* Logitech QuickCam Pro 4000 */ + { USB_DEVICE(0x046D, 0x08B3) }, /* Logitech QuickCam Zoom */ + { USB_DEVICE(0x046D, 0x08B4) }, /* Logitech (reserved) */ + { USB_DEVICE(0x046D, 0x08B5) }, /* Logitech (reserved) */ + { USB_DEVICE(0x046D, 0x08B6) }, /* Logitech (reserved) */ + { USB_DEVICE(0x046D, 0x08B7) }, /* Logitech (reserved) */ + { USB_DEVICE(0x046D, 0x08B8) }, /* Logitech (reserved) */ { USB_DEVICE(0x055D, 0x9000) }, /* Samsung */ { USB_DEVICE(0x055D, 0x9001) }, { USB_DEVICE(0x041E, 0x400C) }, /* Creative Webcam 5 */ + { USB_DEVICE(0x041E, 0x4011) }, /* Creative Webcam Pro Ex */ { USB_DEVICE(0x04CC, 0x8116) }, /* Afina Eye */ { USB_DEVICE(0x0d81, 0x1910) }, /* Visionite */ { USB_DEVICE(0x0d81, 0x1900) }, @@ -101,11 +108,11 @@ disconnect: usb_pwc_disconnect, /* disconnect() */ }; -#define MAX_DEV_HINTS 10 +#define MAX_DEV_HINTS 20 +#define MAX_ISOC_ERRORS 20 static int default_size = PSZ_QCIF; static int default_fps = 10; -static int default_palette = VIDEO_PALETTE_YUV420P; /* This format is understood by most tools */ static int default_fbufs = 3; /* Default number of frame buffers */ static int default_mbufs = 2; /* Default number of mmap() buffers */ int pwc_trace = TRACE_MODULE | TRACE_FLOW | TRACE_PWCX; @@ -119,9 +126,6 @@ struct pwc_device *pdev; } device_hint[MAX_DEV_HINTS]; -static struct semaphore mem_lock; -static void *mem_leak = NULL; /* For delayed kfree()s. See below */ - /***/ static int pwc_video_open(struct video_device *vdev, int mode); @@ -383,50 +387,50 @@ the user program. The first scheme involves the ISO buffers (called thus since they transport ISO data from the USB controller), and not really interesting. Suffices to say the data from this buffer is quickly - gathered in an interrupt handler (pwc_isoc_handler) and placed into the + gathered in an interrupt handler (pwc_isoc_handler) and placed into the frame buffer. - + The frame buffer is the second scheme, and is the central element here. It collects the data from a single frame from the camera (hence, the name). Frames are delimited by the USB camera with a short USB packet, so that's easy to detect. The frame buffers form a list that is filled - by the camera+USB controller and drained by the user process through + by the camera+USB controller and drained by the user process through either read() or mmap(). - + The image buffer is the third scheme, in which frames are decompressed - and possibly converted into planar format. For mmap() there is more than + and converted into planar format. For mmap() there is more than one image buffer available. - The frame buffers provide the image buffering, in case the user process - is a bit slow. This introduces lag and some undesired side-effects. - The problem arises when the frame buffer is full. I used to drop the last - frame, which makes the data in the queue stale very quickly. But dropping + The frame buffers provide the image buffering. In case the user process + is a bit slow, this introduces lag and some undesired side-effects. + The problem arises when the frame buffer is full. I used to drop the last + frame, which makes the data in the queue stale very quickly. But dropping the frame at the head of the queue proved to be a litte bit more difficult. I tried a circular linked scheme, but this introduced more problems than it solved. Because filling and draining are completely asynchronous processes, this requires some fiddling with pointers and mutexes. - + Eventually, I came up with a system with 2 lists: an 'empty' frame list and a 'full' frame list: * Initially, all frame buffers but one are on the 'empty' list; the one remaining buffer is our initial fill frame. - * If a frame is needed for filling, we take it from the 'empty' list, - unless that list is empty, in which case we take the buffer at the - head of the 'full' list. - * When our fill buffer has been filled, it is appended to the 'full' + * If a frame is needed for filling, we try to take it from the 'empty' + list, unless that list is empty, in which case we take the buffer at + the head of the 'full' list. + * When our fill buffer has been filled, it is appended to the 'full' list. - * If a frame is needed by read() or mmap(), it is taken from the head of + * If a frame is needed by read() or mmap(), it is taken from the head of the 'full' list, handled, and then appended to the 'empty' list. If no buffer is present on the 'full' list, we wait. The advantage is that the buffer that is currently being decompressed/ - converted, is on neither list, and thus not in our way (any other scheme + converted, is on neither list, and thus not in our way (any other scheme I tried had the problem of old data lingering in the queue). Whatever strategy you choose, it always remains a tradeoff: with more frame buffers the chances of a missed frame are reduced. On the other - hand, on slower machines it introduces lag because the queue will + hand, on slower machines it introduces lag because the queue will always be full. */ @@ -437,7 +441,7 @@ { int ret; unsigned long flags; - + ret = 0; spin_lock_irqsave(&pdev->ptrlock, flags); if (pdev->fill_frame != NULL) { @@ -454,11 +458,11 @@ if (pdev->empty_frames != NULL) { /* We have empty frames available. That's easy */ pdev->fill_frame = pdev->empty_frames; - pdev->empty_frames = pdev->empty_frames->next; + pdev->empty_frames = pdev->empty_frames->next; } else { /* Hmm. Take it from the full list */ -#if PWC_DEBUG +#if PWC_DEBUG /* sanity check */ if (pdev->full_frames == NULL) { Err("Neither empty or full frames available!\n"); @@ -478,11 +482,11 @@ spin_unlock_irqrestore(&pdev->ptrlock, flags); return ret; } - + /** - \brief Reset all buffers, pointers and lists, except for the image_used[] buffer. - + \brief Reset all buffers, pointers and lists, except for the image_used[] buffer. + If the image_used[] buffer is cleared too, mmap()/VIDIOCSYNC will run into trouble. */ static void pwc_reset_buffers(struct pwc_device *pdev) @@ -519,7 +523,7 @@ { int ret = 0; unsigned long flags; - + spin_lock_irqsave(&pdev->ptrlock, flags); /* First grab our read_frame; this is removed from all lists, so we can release the lock after this without problems */ @@ -542,7 +546,7 @@ Trace(TRACE_SEQUENCE, "Decompressing frame %d\n", pdev->read_frame->sequence); #endif /* Decompression is a lenghty process, so it's outside of the lock. - This gives the isoc_handler the opportunity to fill more frames + This gives the isoc_handler the opportunity to fill more frames in the mean time. */ spin_unlock_irqrestore(&pdev->ptrlock, flags); @@ -566,7 +570,7 @@ } /** - \brief Advance pointers of image buffer (after each user request) + \brief Advance pointers of image buffer (after each user request) */ static inline void pwc_next_image(struct pwc_device *pdev) { @@ -574,22 +578,6 @@ pdev->fill_image = (pdev->fill_image + 1) % default_mbufs; } -/* 2001-10-14: YUV420P is the only palette remaining. */ -static int pwc_set_palette(struct pwc_device *pdev, int pal) -{ - if ( pal == VIDEO_PALETTE_YUV420P -#if PWC_DEBUG - || pal == VIDEO_PALETTE_RAW -#endif - ) { - pdev->vpalette = pal; - pwc_set_image_buffer_size(pdev); - return 0; - } - Trace(TRACE_READ, "Palette %d not supported.\n", pal); - return -1; -} - /* This gets called for the Isochronous pipe (video). This is done in @@ -601,14 +589,15 @@ int i, fst, flen; int awake; struct pwc_frame_buf *fbuf; - unsigned char *fillptr, *iso_buf; + unsigned char *fillptr = 0, *iso_buf = 0; + awake = 0; pdev = (struct pwc_device *)urb->context; if (pdev == NULL) { Err("isoc_handler() called with NULL device?!\n"); return; } -#ifdef PWC_MAGIC +#ifdef PWC_MAGIC if (pdev->magic != PWC_MAGIC) { Err("isoc_handler() called with bad magic!\n"); return; @@ -619,33 +608,51 @@ return; } if (urb->status != -EINPROGRESS && urb->status != 0) { - char *errmsg; - + const char *errmsg; + errmsg = "Unknown"; switch(urb->status) { case -ENOSR: errmsg = "Buffer error (overrun)"; break; case -EPIPE: errmsg = "Stalled (device not responding)"; break; case -EOVERFLOW: errmsg = "Babble (bad cable?)"; break; case -EPROTO: errmsg = "Bit-stuff error (bad cable?)"; break; - case -EILSEQ: errmsg = "CRC/Timeout"; break; + case -EILSEQ: errmsg = "CRC/Timeout (could be anything)"; break; case -ETIMEDOUT: errmsg = "NAK (device does not respond)"; break; } Trace(TRACE_FLOW, "pwc_isoc_handler() called with status %d [%s].\n", urb->status, errmsg); - return; + /* Give up after a number of contiguous errors on the USB bus. + Appearantly something is wrong so we simulate an unplug event. + */ + if (++pdev->visoc_errors > MAX_ISOC_ERRORS) + { + Info("Too many ISOC errors, bailing out.\n"); + pdev->error_status = EIO; + awake = 1; + } + else + return; // better luck next time } fbuf = pdev->fill_frame; if (fbuf == NULL) { Err("pwc_isoc_handler without valid fill frame.\n"); + awake = 1; + } + else { + fillptr = fbuf->data + fbuf->filled; + } + /* Premature wakeup */ + if (awake) { wake_up_interruptible(&pdev->frameq); return; } - fillptr = fbuf->data + fbuf->filled; - awake = 0; + + /* Reset ISOC error counter. We did get here, after all. */ + pdev->visoc_errors = 0; /* vsync: 0 = don't copy data 1 = sync-hunt - 2 = synched + 2 = synched */ /* Compact data */ for (i = 0; i < urb->number_of_packets; i++) { @@ -674,7 +681,7 @@ if (flen < pdev->vlast_packet_size) { /* Shorter packet... We probably have the end of an image-frame; wake up read() process and let select()/poll() do something. - Decompression is done in user time over there. + Decompression is done in user time over there. */ if (pdev->vsync == 2) { /* The ToUCam Fun CMOS sensor causes the firmware to send 2 or 3 bogus @@ -731,7 +738,7 @@ else { /* Send only once per EOF */ awake = 1; /* delay wake_ups */ - + /* Find our next frame to fill. This will always succeed, since we * nick a frame from either empty or full list, but if we had to * take it from the full list, it means a frame got dropped. @@ -764,7 +771,7 @@ if (iso_error < 20) Trace(TRACE_FLOW, "Iso frame %d of USB has error %d\n", i, fst); } -#endif +#endif } if (awake) wake_up_interruptible(&pdev->frameq); @@ -893,8 +900,10 @@ } } - /* Stop camera, but only if we are sure the camera is still there */ - if (!pdev->unplugged) { + /* Stop camera, but only if we are sure the camera is still there (unplug + is signalled by EPIPE) + */ + if (pdev->error_status && pdev->error_status != EPIPE) { Trace(TRACE_OPEN, "Setting alternate interface 0.\n"); usb_set_interface(pdev->udev, 0, 0); } @@ -922,28 +931,6 @@ } -static inline void set_mem_leak(void *ptr) -{ - down(&mem_lock); - if (mem_leak != NULL) - Err("Memleak: overwriting mem_leak pointer!\n"); - Trace(TRACE_MEMORY, "Setting mem_leak to 0x%p.\n", ptr); - mem_leak = ptr; - up(&mem_lock); -} - -static inline void free_mem_leak(void) -{ - down(&mem_lock); - if (mem_leak != NULL) { - Trace(TRACE_MEMORY, "Freeing mem_leak ptr 0x%p.\n", mem_leak); - kfree(mem_leak); - mem_leak = NULL; - } - up(&mem_lock); -} - - /***************************************************************************/ /* Video4Linux functions */ @@ -967,8 +954,8 @@ Trace(TRACE_OPEN, "Doing first time initialization.\n"); pdev->usb_init = 1; - if (pwc_trace & TRACE_OPEN) { - /* Query CMOS sensor type */ + { + /* Query sensor type */ const char *sensor_type = NULL; i = pwc_get_cmos_sensor(pdev); @@ -987,7 +974,7 @@ default: sensor_type = "unknown type of sensor"; break; } if (sensor_type != NULL) - Info("This %s camera is equipped with a %s (%d).\n", pdev->vdev->name, sensor_type, i); + Info("This %s camera is equipped with a %s (%d).\n", pdev->vdev.name, sensor_type, i); } } @@ -1022,24 +1009,20 @@ pdev->vframe_count = 0; pdev->vframes_dumped = 0; pdev->vframes_error = 0; - pdev->vpalette = default_palette; -#if PWC_DEBUG + pdev->visoc_errors = 0; + pdev->error_status = 0; +#if PWC_DEBUG pdev->sequence = 0; #endif /* Set some defaults */ pdev->vsnapshot = 0; - if (pdev->type == 730 || pdev->type == 740 || pdev->type == 750) - pdev->vsize = PSZ_QSIF; - else - pdev->vsize = PSZ_QCIF; - pdev->vframes = 10; - - /* Start iso pipe for video; first try user-supplied size/fps, if - that fails try QCIF/10 or QSIF/10 (a reasonable default), - then give up + + /* Start iso pipe for video; first try the last used video size + (or the default one); if that fails try QCIF/10 or QSIF/10; + it that fails too, give up. */ - i = pwc_set_video_mode(pdev, pwc_image_sizes[default_size].x, pwc_image_sizes[default_size].y, default_fps, pdev->vcompression, 0); + i = pwc_set_video_mode(pdev, pwc_image_sizes[pdev->vsize].x, pwc_image_sizes[pdev->vsize].y, pdev->vframes, pdev->vcompression, 0); if (i) { Trace(TRACE_OPEN, "First attempt at set_video_mode failed.\n"); if (pdev->type == 730 || pdev->type == 740 || pdev->type == 750) @@ -1091,46 +1074,41 @@ if (pdev->vframe_count > 20) Info("Closing video device: %d frames received, dumped %d frames, %d frames with errors.\n", pdev->vframe_count, pdev->vframes_dumped, pdev->vframes_error); - /* Free isoc URBs, stop camera */ + if (pdev->decompressor != NULL) { + pdev->decompressor->exit(); + pdev->decompressor->unlock(); + pdev->decompressor = NULL; + } + pwc_isoc_cleanup(pdev); + pwc_free_buffers(pdev); - if (!pdev->unplugged) { - /* Turn LEDs off */ + /* Turn off LEDS and power down camera, but only when not unplugged */ + if (pdev->error_status != EPIPE) { if (pwc_set_leds(pdev, 0, 0) < 0) Info("Failed to set LED on/off time.\n"); - /* Power down camera to save energy */ if (power_save) { i = pwc_camera_power(pdev, 0); if (i < 0) Err("Failed to power down camera (%d)\n", i); } } - pdev->vopen = 0; - if (pdev->decompressor != NULL) { - pdev->decompressor->exit(); - pdev->decompressor->unlock(); - } - pwc_free_buffers(pdev); - - /* wake up _disconnect() routine */ - if (pdev->unplugged) - wake_up(&pdev->remove_ok); Trace(TRACE_OPEN, "<< video_close()\n"); } /* * FIXME: what about two parallel reads ???? * ANSWER: Not supported. You can't open the device more than once, - despite what the V4L1 interface says. First, I don't see - the need, second there's no mechanism of alerting the + despite what the V4L1 interface says. First, I don't see + the need, second there's no mechanism of alerting the 2nd/3rd/... process of events like changing image size. - And I don't see the point of blocking that for the + And I don't see the point of blocking that for the 2nd/3rd/... process. In multi-threaded environments reading parallel from any device is tricky anyhow. */ - + static long pwc_video_read(struct video_device *vdev, char *buf, unsigned long count, int noblock) { struct pwc_device *pdev; @@ -1142,17 +1120,20 @@ pdev = vdev->priv; if (pdev == NULL) return -EFAULT; - if (pdev->unplugged) { - Info("pwc_video_read: Device got unplugged (1).\n"); - return -EPIPE; /* unplugged device! */ - } + if (pdev->error_status) + return -pdev->error_status; /* Something happened, report what. */ /* In case we're doing partial reads, we don't have to wait for a frame */ if (pdev->image_read_pos == 0) { /* Do wait queueing according to the (doc)book */ add_wait_queue(&pdev->frameq, &wait); - set_current_state(TASK_INTERRUPTIBLE); while (pdev->full_frames == NULL) { + /* Check for unplugged/etc. here */ + if (pdev->error_status) { + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + return -pdev->error_status ; + } if (noblock) { remove_wait_queue(&pdev->frameq, &wait); set_current_state(TASK_RUNNING); @@ -1168,8 +1149,8 @@ } remove_wait_queue(&pdev->frameq, &wait); set_current_state(TASK_RUNNING); - - /* Decompress [, convert] and release frame */ + + /* Decompress and release frame */ if (pwc_handle_frame(pdev)) return -EFAULT; } @@ -1191,35 +1172,33 @@ static long pwc_video_write(struct video_device *vdev, const char *buf, unsigned long count, int noblock) { - return -EINVAL; + return -EINVAL; } static unsigned int pwc_video_poll(struct video_device *vdev, struct file *file, poll_table *wait) { struct pwc_device *pdev; - + if (vdev == NULL) return -EFAULT; pdev = vdev->priv; if (pdev == NULL) return -EFAULT; - + poll_wait(file, &pdev->frameq, wait); - if (pdev->unplugged) { - Info("pwc_video_poll: Device got unplugged.\n"); + if (pdev->error_status) return POLLERR; - } if (pdev->full_frames != NULL) /* we have frames waiting */ return (POLLIN | POLLRDNORM); return 0; } - + static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) { struct pwc_device *pdev; DECLARE_WAITQUEUE(wait, current); - + if (vdev == NULL) return -EFAULT; pdev = vdev->priv; @@ -1228,7 +1207,7 @@ switch (cmd) { /* Query cabapilities */ - case VIDIOCGCAP: + case VIDIOCGCAP: { struct video_capability caps; @@ -1314,7 +1293,7 @@ else p.colour = 0xffff; p.depth = 24; - p.palette = pdev->vpalette; + p.palette = VIDEO_PALETTE_YUV420P; p.hue = 0xFFFF; /* N/A */ if (copy_to_user(arg, &p, sizeof(p))) @@ -1341,9 +1320,8 @@ pwc_set_contrast(pdev, p.contrast); pwc_set_gamma(pdev, p.whiteness); pwc_set_saturation(pdev, p.colour); - if (p.palette && p.palette != pdev->vpalette) { - if (pwc_set_palette(pdev, p.palette) < 0) - return -EINVAL; + if (p.palette && p.palette != VIDEO_PALETTE_YUV420P) { + return -EINVAL; } break; } @@ -1436,9 +1414,8 @@ various palettes... The driver doesn't support such small images, so I'm working around it. */ - if (vm.format && vm.format != pdev->vpalette) - if (pwc_set_palette(pdev, vm.format) < 0) - return -EINVAL; + if (vm.format && vm.format != VIDEO_PALETTE_YUV420P) + return -EINVAL; if ((vm.width != pdev->view.x || vm.height != pdev->view.y) && (vm.width >= pdev->view_min.x && vm.height >= pdev->view_min.y)) { @@ -1458,7 +1435,7 @@ /* Okay, we're done here. In the SYNC call we wait until a frame comes available, then expand image into the given buffer. - In contrast to the CPiA cam the Philips cams deliver a + In contrast to the CPiA cam the Philips cams deliver a constant stream, almost like a grabber card. Also, we have separate buffers for the rawdata and the image, meaning we can nearly always expand into the requested buffer. @@ -1498,27 +1475,26 @@ return -EINVAL; /* Add ourselves to the frame wait-queue. - + In the loop, check for error conditions and signals. + FIXME: needs auditing for safety. - QUSTION: In what respect? I think that using the - frameq is safe now. + QUESTION: In what respect? I think that using the + frameq is safe now. */ add_wait_queue(&pdev->frameq, &wait); - set_current_state(TASK_INTERRUPTIBLE); while (pdev->full_frames == NULL) { - if (pdev->unplugged) { + if (pdev->error_status) { remove_wait_queue(&pdev->frameq, &wait); set_current_state(TASK_RUNNING); - return -ENODEV; + return -pdev->error_status; } - if (signal_pending(current)) { remove_wait_queue(&pdev->frameq, &wait); set_current_state(TASK_RUNNING); return -ERESTARTSYS; } - set_current_state(TASK_INTERRUPTIBLE); schedule(); + set_current_state(TASK_INTERRUPTIBLE); } remove_wait_queue(&pdev->frameq, &wait); set_current_state(TASK_RUNNING); @@ -1572,7 +1548,7 @@ { struct video_unit vu; - vu.video = pdev->vdev->minor & 0x3F; + vu.video = pdev->vdev.minor & 0x3F; vu.audio = -1; /* not known yet */ vu.vbi = -1; vu.radio = -1; @@ -1623,14 +1599,11 @@ static void *usb_pwc_probe(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *id) { struct pwc_device *pdev = NULL; - struct video_device *vdev; int vendor_id, product_id, type_id; int i, hint; int video_nr = -1; /* default: use next available device */ char serial_number[30], *name; - free_mem_leak(); - /* Check if we can handle this device */ Trace(TRACE_PROBE, "probe() called [%04X %04X], if %d\n", udev->descriptor.idVendor, udev->descriptor.idProduct, ifnum); @@ -1691,6 +1664,11 @@ name = "Philips 750 webcam"; type_id = 750; break; + case 0x0313: + Info("Philips PCVC720K/40 (ToUCam XS) USB webcam detected.\n"); + name = "Philips 720 webcam"; + type_id = 720; + break; default: return NULL; break; @@ -1713,12 +1691,12 @@ case 0x08b0: Info("Logitech QuickCam Pro 3000 USB webcam detected.\n"); name = "Logitech QuickCam Pro 3000"; - type_id = 730; + type_id = 740; /* CCD sensor */ break; case 0x08b1: Info("Logitech QuickCam for Notebook Pro USB webcam detected.\n"); name = "Logitech QuickCam Notebook Pro"; - type_id = 740; /* ?? unknown sensor */ + type_id = 740; /* CCD sensor */ break; case 0x08b2: Info("Logitech QuickCam 4000 Pro USB webcam detected.\n"); @@ -1730,6 +1708,15 @@ name = "Logitech QuickCam Zoom"; type_id = 740; /* CCD sensor */ break; + case 0x08b4: + case 0x08b5: + case 0x08b6: + case 0x08b7: + case 0x08b8: + Info("Logitech QuickCam detected (reserved ID).\n"); + name = "Logitech QuickCam (res.)"; + type_id = 730; /* Assuming CMOS */ + break; default: return NULL; break; @@ -1763,6 +1750,11 @@ name = "Creative Labs Webcam 5"; type_id = 730; break; + case 0x4011: + Info("Creative Labs Webcam Pro Ex detected.\n"); + name = "Creative Labs Webcam Pro Ex"; + type_id = 740; + break; default: return NULL; break; @@ -1816,26 +1808,20 @@ memset(pdev, 0, sizeof(struct pwc_device)); pdev->type = type_id; pwc_construct(pdev); + pdev->vsize = default_size; + pdev->vframes = default_fps; init_MUTEX(&pdev->modlock); pdev->ptrlock = SPIN_LOCK_UNLOCKED; pdev->udev = udev; init_waitqueue_head(&pdev->frameq); - init_waitqueue_head(&pdev->remove_ok); pdev->vcompression = pwc_preferred_compression; - /* Now hook it up to the video subsystem */ - vdev = kmalloc(sizeof(struct video_device), GFP_KERNEL); - if (vdev == NULL) { - Err("Oops, could not allocate memory for video_device.\n"); - return NULL; - } - memcpy(vdev, &pwc_template, sizeof(pwc_template)); - strcpy(vdev->name, name); - SET_MODULE_OWNER(vdev); - pdev->vdev = vdev; - vdev->priv = pdev; + memcpy(&pdev->vdev, &pwc_template, sizeof(pwc_template)); + strcpy(pdev->vdev.name, name); + SET_MODULE_OWNER(&pdev->vdev); + pdev->vdev.priv = pdev; pdev->release = udev->descriptor.bcdDevice; Trace(TRACE_PROBE, "Release: %04x\n", pdev->release); @@ -1854,14 +1840,14 @@ } } - i = video_register_device(vdev, VFL_TYPE_GRABBER, video_nr); + i = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, video_nr); if (i < 0) { Err("Failed to register as video device (%d).\n", i); + kfree(pdev); /* Oops, no memory leaks please */ return NULL; } else { - Trace(TRACE_PROBE, "Registered video struct at 0x%p.\n", vdev); - Info("Registered as /dev/video%d.\n", vdev->minor & 0x3F); + Info("Registered as /dev/video%d.\n", pdev->vdev.minor & 0x3F); } /* occupy slot */ if (hint < MAX_DEV_HINTS) @@ -1876,75 +1862,56 @@ { struct pwc_device *pdev; int hint; - DECLARE_WAITQUEUE(wait, current); lock_kernel(); - free_mem_leak(); - pdev = (struct pwc_device *)ptr; if (pdev == NULL) { Err("pwc_disconnect() Called without private pointer.\n"); + unlock_kernel(); return; } if (pdev->udev == NULL) { Err("pwc_disconnect() already called for %p\n", pdev); + unlock_kernel(); return; } if (pdev->udev != udev) { Err("pwc_disconnect() Woops: pointer mismatch udev/pdev.\n"); + unlock_kernel(); return; } -#ifdef PWC_MAGIC +#ifdef PWC_MAGIC if (pdev->magic != PWC_MAGIC) { Err("pwc_disconnect() Magic number failed. Consult your scrolls and try again.\n"); + unlock_kernel(); return; } #endif - - pdev->unplugged = 1; - if (pdev->vdev != NULL) { - add_wait_queue(&pdev->remove_ok, &wait); - set_current_state(TASK_UNINTERRUPTIBLE); - Trace(TRACE_PROBE, "Unregistering video device.\n"); - video_unregister_device(pdev->vdev); - if (pdev->vopen) { - Info("Disconnected while device/video is open!\n"); - - /* Wake up any processes that might be waiting for - a frame, let them return an error condition - */ - wake_up(&pdev->frameq); - - /* Wait until we get a 'go' from _close(). This used - to have a gigantic race condition, since we kfree() - stuff here, but we have to wait until close() - is finished. - */ - - Trace(TRACE_PROBE, "Sleeping on remove_ok.\n"); - /* ... wait ... */ - schedule(); - Trace(TRACE_PROBE, "Done sleeping.\n"); - set_mem_leak(pdev->vdev); - pdev->vdev = NULL; - } - else { - /* Normal disconnect; remove from available devices */ - kfree(pdev->vdev); - pdev->vdev = NULL; - } - remove_wait_queue(&pdev->remove_ok, &wait); - set_current_state(TASK_RUNNING); + + /* We got unplugged; this is signalled by an EPIPE error code */ + if (pdev->vopen) { + Info("Disconnected while webcam is in use!\n"); + pdev->error_status = EPIPE; } + + /* Alert waiting processes */ + wake_up_interruptible(&pdev->frameq); + /* Wait until device is closed */ + while (pdev->vopen) + schedule(); + /* Device is now closed, so we can safely unregister it */ + Trace(TRACE_PROBE, "Unregistering video device in disconnect().\n"); + video_unregister_device(&pdev->vdev); + + /* Free memory (don't set pdev to 0 just yet) */ + kfree(pdev); /* search device_hint[] table if we occupy a slot, by any chance */ for (hint = 0; hint < MAX_DEV_HINTS; hint++) if (device_hint[hint].pdev == pdev) device_hint[hint].pdev = NULL; - pdev->udev = NULL; unlock_kernel(); - kfree(pdev); } @@ -1973,7 +1940,7 @@ static int trace = -1; static int compression = -1; static int leds[2] = { -1, -1 }; -static char *dev_hint[10] = { }; +static char *dev_hint[MAX_DEV_HINTS] = { }; MODULE_PARM(size, "s"); MODULE_PARM_DESC(size, "Initial image size. One of sqcif, qsif, qcif, sif, cif, vga"); @@ -1991,7 +1958,7 @@ MODULE_PARM_DESC(compression, "Preferred compression quality. Range 0 (uncompressed) to 3 (high compression)"); MODULE_PARM(leds, "2i"); MODULE_PARM_DESC(leds, "LED on,off time in milliseconds"); -MODULE_PARM(dev_hint, "0-10s"); +MODULE_PARM(dev_hint, "0-20s"); MODULE_PARM_DESC(dev_hint, "Device node hints"); MODULE_DESCRIPTION("Philips USB & OEM webcam driver"); @@ -2133,14 +2100,12 @@ device_hint[i].type = 0; /* not filled */ } /* ..for MAX_DEV_HINTS */ - init_MUTEX(&mem_lock); Trace(TRACE_PROBE, "Registering driver at address 0x%p.\n", &pwc_driver); return usb_register(&pwc_driver); } static void __exit usb_pwc_exit(void) { - free_mem_leak(); Trace(TRACE_MODULE, "Deregistering driver.\n"); usb_deregister(&pwc_driver); Info("Philips webcam module removed.\n"); diff -Nru a/drivers/usb/pwc-ioctl.h b/drivers/usb/pwc-ioctl.h --- a/drivers/usb/pwc-ioctl.h Thu Aug 28 14:50:23 2003 +++ b/drivers/usb/pwc-ioctl.h Thu Aug 28 14:50:23 2003 @@ -1,7 +1,7 @@ #ifndef PWC_IOCTL_H #define PWC_IOCTL_H -/* (C) 2001-2002 Nemosoft Unv. webcam@smcc.demon.nl +/* (C) 2001-2003 Nemosoft Unv. webcam@smcc.demon.nl 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 diff -Nru a/drivers/usb/pwc-misc.c b/drivers/usb/pwc-misc.c --- a/drivers/usb/pwc-misc.c Thu Aug 28 14:50:23 2003 +++ b/drivers/usb/pwc-misc.c Thu Aug 28 14:50:23 2003 @@ -1,6 +1,6 @@ /* Linux driver for Philips webcam Various miscellaneous functions and tables. - (C) 1999-2002 Nemosoft Unv. (webcam@smcc.demon.nl) + (C) 1999-2003 Nemosoft Unv. (webcam@smcc.demon.nl) 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 @@ -51,7 +51,6 @@ } return find; } - /* initialize variables depending on type */ void pwc_construct(struct pwc_device *pdev) { @@ -81,6 +80,7 @@ pdev->frame_header_size = 0; pdev->frame_trailer_size = 0; break; + case 720: case 730: case 740: case 750: diff -Nru a/drivers/usb/pwc-uncompress.c b/drivers/usb/pwc-uncompress.c --- a/drivers/usb/pwc-uncompress.c Thu Aug 28 14:50:23 2003 +++ b/drivers/usb/pwc-uncompress.c Thu Aug 28 14:50:23 2003 @@ -1,6 +1,6 @@ /* Linux driver for Philips webcam Decompression frontend. - (C) 1999-2002 Nemosoft Unv. (webcam@smcc.demon.nl) + (C) 1999-2003 Nemosoft Unv. (webcam@smcc.demon.nl) 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 @@ -32,7 +32,7 @@ /* Should the pwc_decompress structure ever change, we increase the version number so that we don't get nasty surprises, or can - dynamicly adjust our structure. + dynamically adjust our structure. */ const int pwc_decompressor_version = PWC_MAJOR; @@ -98,14 +98,6 @@ if (!image) return -EFAULT; -#if PWC_DEBUG - /* This is a quickie */ - if (pdev->vpalette == VIDEO_PALETTE_RAW) { - memcpy(image, fbuf->data, pdev->frame_size); - return 0; - } -#endif - yuv = fbuf->data + pdev->frame_header_size; /* Skip header */ if (pdev->vbandlength == 0) { /* Uncompressed mode. We copy the data into the output buffer, @@ -113,8 +105,6 @@ size). Unfortunately we have to do a bit of byte stuffing to get the desired output format/size. */ - switch (pdev->vpalette) { - case VIDEO_PALETTE_YUV420P: /* * We do some byte shuffling here to go from the * native format to YUV420P. @@ -149,11 +139,6 @@ else dstu += (stride >> 1); } - break; - default: - Err("Unsupported palette!"); - break; - } } else { /* Compressed; the decompressor routines will write the data diff -Nru a/drivers/usb/pwc-uncompress.h b/drivers/usb/pwc-uncompress.h --- a/drivers/usb/pwc-uncompress.h Thu Aug 28 14:50:23 2003 +++ b/drivers/usb/pwc-uncompress.h Thu Aug 28 14:50:23 2003 @@ -1,4 +1,4 @@ -/* (C) 1999-2002 Nemosoft Unv. (webcam@smcc.demon.nl) +/* (C) 1999-2003 Nemosoft Unv. (webcam@smcc.demon.nl) 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 diff -Nru a/drivers/usb/pwc.h b/drivers/usb/pwc.h --- a/drivers/usb/pwc.h Thu Aug 28 14:50:23 2003 +++ b/drivers/usb/pwc.h Thu Aug 28 14:50:23 2003 @@ -1,4 +1,4 @@ -/* (C) 1999-2002 Nemosoft Unv. (webcam@smcc.demon.nl) +/* (C) 1999-2003 Nemosoft Unv. (webcam@smcc.demon.nl) 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 @@ -60,8 +60,8 @@ /* Version block */ #define PWC_MAJOR 8 -#define PWC_MINOR 10 -#define PWC_VERSION "8.10" +#define PWC_MINOR 11 +#define PWC_VERSION "8.11" #define PWC_NAME "pwc" /* Turn certain features on/off */ @@ -82,7 +82,7 @@ #define PWC_FRAME_SIZE (460800 + TOUCAM_HEADER_SIZE + TOUCAM_TRAILER_SIZE) /* Absolute maximum number of buffers available for mmap() */ -#define MAX_IMAGES 4 +#define MAX_IMAGES 10 struct pwc_coord { @@ -112,6 +112,7 @@ struct pwc_device { + struct video_device vdev; #ifdef PWC_MAGIC int magic; #endif @@ -120,22 +121,21 @@ int type; /* type of cam (645, 646, 675, 680, 690) */ int release; /* release number */ - int unplugged; /* set when the plug is pulled */ + int error_status; /* set when something goes wrong with the cam (unplugged, USB errors) */ int usb_init; /* set when the cam has been initialized over USB */ /*** Video data ***/ int vopen; /* flag */ - struct video_device *vdev; int vendpoint; /* video isoc endpoint */ int vcinterface; /* video control interface */ int valternate; /* alternate interface needed */ int vframes, vsize; /* frames-per-second & size (see PSZ_*) */ - int vpalette; /* YUV */ int vframe_count; /* received frames */ int vframes_dumped; /* counter for dumped frames */ int vframes_error; /* frames received in error */ int vmax_packet_size; /* USB maxpacket size */ int vlast_packet_size; /* for frame synchronisation */ + int visoc_errors; /* number of contiguous ISOC errors */ int vcompression; /* desired compression factor */ int vbandlength; /* compressed band length; 0 is uncompressed */ char vsnapshot; /* snapshot mode */ @@ -147,15 +147,15 @@ 2. data is synchronized and packed into a frame buffer 3a. in case data is compressed, decompress it directly into image buffer 3b. in case data is uncompressed, copy into image buffer with viewport - 4. data is transfered to the user process + 4. data is transferred to the user process - Note that MAX_ISO_BUFS != MAX_FRAMES != MAX_IMAGES.... + Note that MAX_ISO_BUFS != MAX_FRAMES != MAX_IMAGES.... We have in effect a back-to-back-double-buffer system. */ /* 1: isoc */ struct pwc_iso_buf sbuf[MAX_ISO_BUFS]; char iso_init; - + /* 2: frame */ struct pwc_frame_buf *fbuf; /* all frames */ struct pwc_frame_buf *empty_frames, *empty_frames_tail; /* all empty frames */ @@ -168,7 +168,7 @@ #if PWC_DEBUG int sequence; /* Debugging aid */ #endif - + /* 3: decompression */ struct pwc_decompressor *decompressor; /* function block with decompression routines */ void *decompress_data; /* private data for decompression engine */ @@ -176,7 +176,7 @@ /* 4: image */ /* We have an 'image' and a 'view', where 'image' is the fixed-size image as delivered by the camera, and 'view' is the size requested by the - program. The camera image is centered in this viewport, laced with + program. The camera image is centered in this viewport, laced with a gray or black border. view_min <= image <= view <= view_max; */ int image_mask; /* bitmask of supported sizes */ @@ -196,10 +196,9 @@ /*** Misc. data ***/ wait_queue_head_t frameq; /* When waiting for a frame to finish... */ - wait_queue_head_t remove_ok; /* When we got hot unplugged, we have to avoid a few race conditions */ #if PWC_INT_PIPE void *usb_int_handler; /* for the interrupt endpoint */ -#endif +#endif }; /* Enumeration of image sizes */