--- /dev/null
+diff -urN linux-2.4.20.orig/Documentation/usb/philips.txt linux-2.4.20/Documentation/usb/philips.txt
+--- linux-2.4.20.orig/Documentation/usb/philips.txt Mon Jan 6 22:16:06 2003
++++ linux-2.4.20/Documentation/usb/philips.txt Mon Dec 16 22:19:15 2002
+@@ -1,13 +1,34 @@
+-This file contains some additional information for the Philips webcams.
+-E-mail: webcam@smcc.demon.nl Last updated: 2001-09-24
+-
+-The main webpage for the Philips driver is http://www.smcc.demon.nl/webcam/.
+-It contains a lot of extra information, a FAQ, and the binary plugin
+-'PWCX'. This plugin contains decompression routines that allow you to
+-use higher image sizes and framerates; in addition the webcam uses less
+-bandwidth on the USB bus (handy if you want to run more than 1 camera
+-simultaneously). These routines fall under an NDA, and may therefor not be
+-distributed as source; however, its use is completely optional.
++This file contains some additional information for the Philips and OEM webcams.
++E-mail: webcam@smcc.demon.nl Last updated: 2002-11-28
++Site: http://www.smcc.demon.nl/webcam/
++As of this moment, the following cameras are supported:
++ * Philips PCA645
++ * Philips PCA646
++ * Philips PCVC675
++ * Philips PCVC680
++ * Philips PCVC690
++ * Philips PCVC730
++ * Philips PCVC740
++ * Philips PCVC750
++ * Askey VC010
++ * Creative Labs Webcam 5
++ * Logitech QuickCam 3000 Pro
++ * Logitech QuickCam Notebook
++ * Logitech QuickCam 4000 Pro
++ * Logitech QuickCam Zoom
++ * Samsung MPC-C10
++ * Samsung MPC-C30
++ * Sotec Afina Eye
++ * Visionite VCS-UM100
++ * Visionite VCS-UC300
++
++The main webpage for the Philips driver is at the address above. It contains
++a lot of extra information, a FAQ, and the binary plugin 'PWCX'. This plugin
++contains decompression routines that allow you to use higher image sizes and
++framerates; in addition the webcam uses less bandwidth on the USB bus (handy
++if you want to run more than 1 camera simultaneously). These routines fall
++under a NDA, and may therefor not be distributed as source; however, its use
++is completely optional.
+
+ You can build this code either into your kernel, or as a module. I recommend
+ the latter, since it makes troubleshooting a lot easier. The built-in
+@@ -98,6 +119,9 @@
+ 750). For other cameras this command is silently ignored, and the LED
+ cannot be controlled.
+
++ Finally: this parameters does not take effect UNTIL the first time you
++ open the camera device. Until then, the LED remains on.
++
+ dev_hint
+ A long standing problem with USB devices is their dynamic nature: you
+ never know what device a camera gets assigned; it depends on module load
+@@ -126,7 +150,7 @@
+ other cameras will get the first free
+ available slot (see below).
+
+- dev_hint=645:1,680=2 The PCA645 camera will get /dev/video1,
++ dev_hint=645:1,680:2 The PCA645 camera will get /dev/video1,
+ and a PCVC680 /dev/video2.
+
+ dev_hint=645.0123:3,645.4567:0 The PCA645 camera with serialnumber
+@@ -176,6 +200,7 @@
+
+ 64 0x40 Show viewport and image sizes Off
+
++ 128 0x80 PWCX debugging Off
+
+ For example, to trace the open() & read() fuctions, sum 8 + 4 = 12,
+ so you would supply trace=12 during insmod or modprobe. If
+@@ -189,10 +214,10 @@
+ The fbufs, mbufs and trace parameters are global and apply to all connected
+ cameras. Each camera has its own set of buffers.
+
+-size, fps, palette only specify defaults when you open() the device; this is
+-to accommodate some tools that don't set the size or colour palette. You can
+-change these settings after open() with the Video4Linux ioctl() calls. The
+-default of defaults is QCIF size at 10 fps, BGR order.
++size and fps only specify defaults when you open() the device; this is to
++accommodate some tools that don't set the size. You can change these
++settings after open() with the Video4Linux ioctl() calls. The default of
++defaults is QCIF size at 10 fps, YUV420 palette order.
+
+ The compression parameter is semiglobal; it sets the initial compression
+ preference for all camera's, but this parameter can be set per camera with
+@@ -200,4 +225,3 @@
+
+ All parameters are optional.
+
+-
+diff -urN linux-2.4.20.orig/drivers/usb/pwc-ctrl.c linux-2.4.20/drivers/usb/pwc-ctrl.c
+--- linux-2.4.20.orig/drivers/usb/pwc-ctrl.c Mon Jan 6 22:15:48 2003
++++ linux-2.4.20/drivers/usb/pwc-ctrl.c Mon Dec 16 22:19:15 2002
+@@ -256,8 +256,10 @@
+
+ memcpy(buf, pEntry->mode, 3);
+ ret = send_video_command(pdev->udev, pdev->vendpoint, buf, 3);
+- if (ret < 0)
++ if (ret < 0) {
++ Debug("Failed to send video command... %d\n", ret);
+ return ret;
++ }
+ if (pEntry->compressed && pdev->decompressor != NULL)
+ pdev->decompressor->init(pdev->release, buf, pdev->decompress_data);
+
+@@ -444,8 +446,8 @@
+ Info("Video mode %s@%d fps is only supported with the decompressor module (pwcx).\n", size2name[size], frames);
+ else {
+ Err("Failed to set video mode %s@%d fps; return code = %d\n", size2name[size], frames, ret);
+- return ret;
+ }
++ return ret;
+ }
+ pdev->view.x = width;
+ pdev->view.y = height;
+@@ -997,7 +999,7 @@
+ &buf, 1, HZ / 2);
+
+ if (ret < 0)
+- return ret;
++ return ret;
+
+ return (buf << 8);
+ }
+@@ -1103,12 +1105,7 @@
+ buf[0] = on_value;
+ buf[1] = off_value;
+
+- return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
+- SET_STATUS_CTL,
+- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+- LED_FORMATTER,
+- pdev->vcinterface,
+- &buf, 2, HZ / 2);
++ return SendControlMsg(SET_STATUS_CTL, LED_FORMATTER, 2);
+ }
+
+ int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value)
+@@ -1122,13 +1119,7 @@
+ return 0;
+ }
+
+- ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),
+- GET_STATUS_CTL,
+- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+- LED_FORMATTER,
+- pdev->vcinterface,
+- &buf, 2, HZ / 2);
+-
++ ret = RecvControlMsg(GET_STATUS_CTL, LED_FORMATTER, 2);
+ if (ret < 0)
+ return ret;
+ *on_value = buf[0] * 100;
+@@ -1252,7 +1243,7 @@
+ int ret;
+ unsigned char buf;
+
+- ret = RecvControlMsg(SET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1);
++ ret = RecvControlMsg(GET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1);
+ if (ret < 0)
+ return ret;
+ return buf;
+@@ -1276,10 +1267,9 @@
+ int ret;
+ unsigned char buf;
+
+- ret = RecvControlMsg(SET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1);
++ ret = RecvControlMsg(GET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1);
+ if (ret < 0)
+ return ret;
+-Debug("pwc_get_dynamic_noise = %d\n", buf);
+ return buf;
+ }
+
+@@ -1595,6 +1585,16 @@
+ break;
+ }
+
++ case VIDIOCPWCGREALSIZE:
++ {
++ struct pwc_imagesize size;
++
++ size.width = pdev->image.x;
++ size.height = pdev->image.y;
++ if (copy_to_user(arg, &size, sizeof(size)))
++ ret = -EFAULT;
++ break;
++ }
+
+ default:
+ ret = -ENOIOCTLCMD;
+diff -urN linux-2.4.20.orig/drivers/usb/pwc-if.c linux-2.4.20/drivers/usb/pwc-if.c
+--- linux-2.4.20.orig/drivers/usb/pwc-if.c Mon Jan 6 22:15:48 2003
++++ linux-2.4.20/drivers/usb/pwc-if.c Mon Dec 16 22:19:15 2002
+@@ -42,11 +42,15 @@
+ - Alistar Moire: QuickCam 3000 Pro device/product ID
+ - Tony Hoyle: Creative Labs Webcam 5 device/product ID
+ - Mark Burazin: solving hang in VIDIOCSYNC when camera gets unplugged
+- - Jk Fang: SOTEC device/product ID
++ - Jk Fang: Sotec Afina Eye ID
++ - Xavier Roche: QuickCam Pro 4000 ID
++ - Jens Knudsen: QuickCam Zoom ID
++ - J. Debert: QuickCam for Notebooks ID
+ */
+
+ #include <linux/errno.h>
+ #include <linux/init.h>
++#include <linux/mm.h>
+ #include <linux/module.h>
+ #include <linux/poll.h>
+ #include <linux/slab.h>
+@@ -58,10 +62,6 @@
+ #include "pwc-ioctl.h"
+ #include "pwc-uncompress.h"
+
+-#if !defined(MAP_NR)
+-#define MAP_NR(a) virt_to_page(a)
+-#endif
+-
+ /* Function prototypes and driver templates */
+
+ /* hotplug device table support */
+@@ -76,10 +76,13 @@
+ { USB_DEVICE(0x0471, 0x0311) },
+ { USB_DEVICE(0x0471, 0x0312) },
+ { USB_DEVICE(0x069A, 0x0001) }, /* Askey */
+- { USB_DEVICE(0x046D, 0x08b0) }, /* Logitech */
++ { 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(0x055D, 0x9000) }, /* Samsung */
+ { USB_DEVICE(0x055D, 0x9001) },
+- { USB_DEVICE(0x041E, 0x400C) }, /* Creative */
++ { USB_DEVICE(0x041E, 0x400C) }, /* Creative Webcam 5 */
+ { USB_DEVICE(0x04CC, 0x8116) }, /* Afina Eye */
+ { USB_DEVICE(0x0d81, 0x1910) }, /* Visionite */
+ { USB_DEVICE(0x0d81, 0x1900) },
+@@ -127,7 +130,7 @@
+ static long pwc_video_write(struct video_device *vdev, const char *buf, unsigned long count, int noblock);
+ static unsigned int pwc_video_poll(struct video_device *vdev, struct file *file, poll_table *wait);
+ static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *arg);
+-static int pwc_video_mmap(struct video_device *dev, const char *adr, unsigned long size);
++static int pwc_video_mmap(struct video_device *vdev, const char *adr, unsigned long size);
+
+ static struct video_device pwc_template = {
+ owner: THIS_MODULE,
+@@ -238,7 +241,7 @@
+ int i;
+ void *kbuf;
+
+- Trace(TRACE_MEMORY, "Entering allocate_buffers(%p).\n", pdev);
++ Trace(TRACE_MEMORY, ">> pwc_allocate_buffers(pdev = 0x%p)\n", pdev);
+
+ if (pdev == NULL)
+ return -ENXIO;
+@@ -315,7 +318,7 @@
+
+ kbuf = NULL;
+
+- Trace(TRACE_MEMORY, "Leaving pwc_allocate_buffers().\n");
++ Trace(TRACE_MEMORY, "<< pwc_allocate_buffers()\n");
+ return 0;
+ }
+
+@@ -592,7 +595,7 @@
+ /* This gets called for the Isochronous pipe (video). This is done in
+ * interrupt time, so it has to be fast, not crash, and not stall. Neat.
+ */
+-static void pwc_isoc_handler(struct urb *urb)
++static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs)
+ {
+ struct pwc_device *pdev;
+ int i, fst, flen;
+@@ -612,7 +615,7 @@
+ }
+ #endif
+ if (urb->status == -ENOENT || urb->status == -ECONNRESET) {
+- Trace(TRACE_OPEN, "pwc_isoc_handler(): URB unlinked.\n");
++ Trace(TRACE_OPEN, "pwc_isoc_handler(): URB (%p) unlinked %ssynchronuously.\n", urb, urb->status == -ENOENT ? "" : "a");
+ return;
+ }
+ if (urb->status != -EINPROGRESS && urb->status != 0) {
+@@ -685,9 +688,22 @@
+ #if PWC_DEBUG
+ Debug("Hyundai CMOS sensor bug. Dropping frame %d.\n", fbuf->sequence);
+ #endif
+- pdev->drop_frames = 2;
++ pdev->drop_frames += 2;
+ pdev->vframes_error++;
+ }
++ if ((ptr[0] ^ pdev->vmirror) & 0x01) {
++ if (ptr[0] & 0x01)
++ Info("Snapshot button pressed.\n");
++ else
++ Info("Snapshot button released.\n");
++ }
++ if ((ptr[0] ^ pdev->vmirror) & 0x02) {
++ if (ptr[0] & 0x02)
++ Info("Image is mirrored.\n");
++ else
++ Info("Image is normal.\n");
++ }
++ pdev->vmirror = ptr[0] & 0x03;
+ /* Sometimes the trailer of the 730 is still sent as a 4 byte packet
+ after a short frame; this condition is filtered out specifically. A 4 byte
+ frame doesn't make sense anyway.
+@@ -704,7 +720,7 @@
+ /* In case we were instructed to drop the frame, do so silently.
+ The buffer pointers are not updated either (but the counters are reset below).
+ */
+- if (pdev->drop_frames)
++ if (pdev->drop_frames > 0)
+ pdev->drop_frames--;
+ else {
+ /* Check for underflow first */
+@@ -740,10 +756,14 @@
+ } /* .. flen < last_packet_size */
+ pdev->vlast_packet_size = flen;
+ } /* ..status == 0 */
+-#ifdef PWC_DEBUG
++#if PWC_DEBUG
+ /* This is normally not interesting to the user, unless you are really debugging something */
+- else
+- Trace(TRACE_FLOW, "Iso frame %d of USB has error %d\n", i, fst);
++ else {
++ static int iso_error = 0;
++ iso_error++;
++ if (iso_error < 20)
++ Trace(TRACE_FLOW, "Iso frame %d of USB has error %d\n", i, fst);
++ }
+ #endif
+ }
+ if (awake)
+@@ -758,7 +778,6 @@
+ int i, j, ret;
+
+ struct usb_interface_descriptor *idesc;
+- int cur_alt;
+
+ if (pdev == NULL)
+ return -EFAULT;
+@@ -766,12 +785,11 @@
+ return 0;
+ pdev->vsync = 0;
+ udev = pdev->udev;
+-
++
+ /* Get the current alternate interface, adjust packet size */
+ if (!udev->actconfig)
+ return -EFAULT;
+- cur_alt = udev->actconfig->interface[0].act_altsetting;
+- idesc = &udev->actconfig->interface[0].altsetting[cur_alt];
++ idesc = &udev->actconfig->interface[0].altsetting[pdev->valternate];
+ if (!idesc)
+ return -EFAULT;
+
+@@ -783,12 +801,18 @@
+ break;
+ }
+
+- if (pdev->vmax_packet_size < 0) {
++ if (pdev->vmax_packet_size < 0 || pdev->vmax_packet_size > ISO_MAX_FRAME_SIZE) {
+ Err("Failed to find packet size for video endpoint in current alternate setting.\n");
+ return -ENFILE; /* Odd error, that should be noticable */
+ }
+
++ /* Set alternate interface */
+ ret = 0;
++ Trace(TRACE_OPEN, "Setting alternate interface %d\n", pdev->valternate);
++ ret = usb_set_interface(pdev->udev, 0, pdev->valternate);
++ if (ret < 0)
++ return ret;
++
+ for (i = 0; i < MAX_ISO_BUFS; i++) {
+ urb = usb_alloc_urb(ISO_FRAMES_PER_DESC);
+ if (urb == NULL) {
+@@ -797,6 +821,7 @@
+ break;
+ }
+ pdev->sbuf[i].urb = urb;
++ Trace(TRACE_MEMORY, "Allocated URB at 0x%p\n", urb);
+ }
+ if (ret) {
+ /* De-allocate in reverse order */
+@@ -808,8 +833,7 @@
+ }
+ return ret;
+ }
+-
+-
++
+ /* init URB structure */
+ for (i = 0; i < MAX_ISO_BUFS; i++) {
+ urb = pdev->sbuf[i].urb;
+@@ -826,7 +850,7 @@
+ urb->number_of_packets = ISO_FRAMES_PER_DESC;
+ for (j = 0; j < ISO_FRAMES_PER_DESC; j++) {
+ urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE;
+- urb->iso_frame_desc[j].length = ISO_MAX_FRAME_SIZE;
++ urb->iso_frame_desc[j].length = pdev->vmax_packet_size;
+ }
+ }
+
+@@ -836,11 +860,12 @@
+ if (ret)
+ Err("isoc_init() submit_urb %d failed with error %d\n", i, ret);
+ else
+- Trace(TRACE_OPEN, "pwc_isoc_init(): URB submitted.\n");
++ Trace(TRACE_OPEN, "URB 0x%p submitted.\n", pdev->sbuf[i].urb);
+ }
+
+- /* data should stream in now */
++ /* All is done... */
+ pdev->iso_init = 1;
++ Trace(TRACE_OPEN, "<< pwc_isoc_init()\n");
+ return 0;
+ }
+
+@@ -848,21 +873,34 @@
+ {
+ int i;
+
++ Trace(TRACE_OPEN, ">> pwc_isoc_cleanup()\n");
+ if (pdev == NULL)
+ return;
+- if (!pdev->iso_init)
+- return;
++
++ /* Unlinking ISOC buffers one by one */
++ for (i = 0; i < MAX_ISO_BUFS; i++) {
++ struct urb *urb;
++
++ urb = pdev->sbuf[i].urb;
++ if (urb != 0) {
++ if (pdev->iso_init) {
++ Trace(TRACE_MEMORY, "Unlinking URB %p\n", urb);
++ usb_unlink_urb(urb);
++ }
++ Trace(TRACE_MEMORY, "Freeing URB\n");
++ usb_free_urb(urb);
++ pdev->sbuf[i].urb = NULL;
++ }
++ }
++
+ /* Stop camera, but only if we are sure the camera is still there */
+- if (!pdev->unplugged)
++ if (!pdev->unplugged) {
++ Trace(TRACE_OPEN, "Setting alternate interface 0.\n");
+ usb_set_interface(pdev->udev, 0, 0);
+- /* Unlinking ISOC buffers one by one */
+- for (i = MAX_ISO_BUFS - 1; i >= 0; i--) {
+- pdev->sbuf[i].urb->next = NULL;
+- usb_unlink_urb(pdev->sbuf[i].urb);
+- usb_free_urb(pdev->sbuf[i].urb);
+- pdev->sbuf[i].urb = NULL;
+ }
++
+ pdev->iso_init = 0;
++ Trace(TRACE_OPEN, "<< pwc_isoc_cleanup()\n");
+ }
+
+ int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_fps, int new_compression, int new_snapshot)
+@@ -876,11 +914,10 @@
+ ret = pwc_set_video_mode(pdev, width, height, new_fps, new_compression, new_snapshot);
+ if (ret) /* That failed... restore old mode (we know that worked) */
+ ret = pwc_set_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot);
+- else /* Set (new) alternate interface */
+- ret = usb_set_interface(pdev->udev, 0, pdev->valternate);
+ if (!ret)
+- ret = pwc_isoc_init(pdev);
+- pdev->drop_frames = 1; /* try to avoid garbage during switch */
++ if (pwc_isoc_init(pdev) < 0)
++ Info("Failed to restart ISOC transfers in pwc_try_video_mode.\n");
++ pdev->drop_frames++; /* try to avoid garbage during switch */
+ return ret;
+ }
+
+@@ -915,20 +952,19 @@
+ int i;
+ struct pwc_device *pdev;
+
+- Trace(TRACE_OPEN, "video_open called(0x%p, 0%o).\n", vdev, mode);
++ Trace(TRACE_OPEN, ">> video_open called(vdev = 0x%p).\n", vdev);
+
+ if (vdev == NULL)
+ BUG();
+ pdev = (struct pwc_device *)vdev->priv;
+ if (pdev == NULL)
+ BUG();
++ if (pdev->vopen)
++ return -EBUSY;
+
+ down(&pdev->modlock);
+ if (!pdev->usb_init) {
+ Trace(TRACE_OPEN, "Doing first time initialization.\n");
+- /* Reset camera */
+- if (usb_set_interface(pdev->udev, 0, 0))
+- Info("Failed to set alternate interface to 0.\n");
+ pdev->usb_init = 1;
+
+ if (pwc_trace & TRACE_OPEN) {
+@@ -948,10 +984,10 @@
+ case 0x40: sensor_type = "UPA 1021 sensor"; break;
+ case 0x100: sensor_type = "VGA sensor"; break;
+ case 0x101: sensor_type = "PAL MR sensor"; break;
+- default: sensor_type = "unknown type of sensor"; break;
++ default: sensor_type = "unknown type of sensor"; break;
+ }
+ if (sensor_type != NULL)
+- Info("Thes %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);
+ }
+ }
+
+@@ -1017,16 +1053,9 @@
+ return i;
+ }
+
+- i = usb_set_interface(pdev->udev, 0, pdev->valternate);
+- if (i) {
+- Trace(TRACE_OPEN, "Failed to set alternate interface = %d.\n", i);
+- up(&pdev->modlock);
+- return -EINVAL;
+- }
+ i = pwc_isoc_init(pdev);
+ if (i) {
+ Trace(TRACE_OPEN, "Failed to init ISOC stuff = %d.\n", i);
+- MOD_DEC_USE_COUNT;
+ up(&pdev->modlock);
+ return i;
+ }
+@@ -1039,7 +1068,7 @@
+ if (pdev->decompressor != NULL)
+ pdev->decompressor->lock();
+ up(&pdev->modlock);
+- Trace(TRACE_OPEN, "video_open() returning 0.\n");
++ Trace(TRACE_OPEN, "<< video_open() returns 0.\n");
+ return 0;
+ }
+
+@@ -1049,15 +1078,12 @@
+ struct pwc_device *pdev;
+ int i;
+
+- Trace(TRACE_OPEN, "video_close called(0x%p).\n", vdev);
++ Trace(TRACE_OPEN, ">> video_close called(vdev = 0x%p).\n", vdev);
+
+ pdev = (struct pwc_device *)vdev->priv;
+ if (pdev->vopen == 0)
+ Info("video_close() called on closed device?\n");
+
+- /* Free isoc URBs */
+- pwc_isoc_cleanup(pdev);
+-
+ /* Dump statistics, but only if a reasonable amount of frames were
+ processed (to prevent endless log-entries in case of snap-shot
+ programs)
+@@ -1065,21 +1091,14 @@
+ 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);
+
+- if (pdev->unplugged) {
+- /* The device was unplugged or some other error occured */
+- /* We unregister the video_device */
+- Trace(TRACE_OPEN, "Delayed video device unregistered.\n");
+- video_unregister_device(pdev->vdev);
+- }
+- else {
+- /* Normal close: stop isochronuous and interrupt endpoint */
+- Trace(TRACE_OPEN, "Normal close(): setting interface to 0.\n");
+- usb_set_interface(pdev->udev, 0, 0);
++ /* Free isoc URBs, stop camera */
++ pwc_isoc_cleanup(pdev);
+
++ if (!pdev->unplugged) {
+ /* Turn LEDs off */
+ if (pwc_set_leds(pdev, 0, 0) < 0)
+- Info("Failed to set LED on/off time..\n");
+- /* Power down camere to save energy */
++ 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)
+@@ -1097,6 +1116,7 @@
+ /* wake up _disconnect() routine */
+ if (pdev->unplugged)
+ wake_up(&pdev->remove_ok);
++ Trace(TRACE_OPEN, "<< video_close()\n");
+ }
+
+ /*
+@@ -1495,8 +1515,8 @@
+ set_current_state(TASK_RUNNING);
+ return -ERESTARTSYS;
+ }
+- schedule();
+ set_current_state(TASK_INTERRUPTIBLE);
++ schedule();
+ }
+ remove_wait_queue(&pdev->frameq, &wait);
+ set_current_state(TASK_RUNNING);
+@@ -1574,8 +1594,6 @@
+ Trace(TRACE_MEMORY, "mmap(0x%p, 0x%p, %lu) called.\n", vdev, adr, size);
+ pdev = vdev->priv;
+
+- /* FIXME - audit mmap during a read */
+- /* Nemo: 9 months and 20 kernel revisions later I still don't know what you mean by this :-) */
+ pos = (unsigned long)pdev->image_data;
+ while (size > 0) {
+ page = kvirt_to_pa(pos);
+@@ -1691,10 +1709,25 @@
+ else if (vendor_id == 0x046d) {
+ switch(product_id) {
+ case 0x08b0:
+- Info("Logitech QuickCam 3000 Pro USB webcam detected.\n");
+- name = "Logitech QuickCam 3000 Pro";
++ Info("Logitech QuickCam Pro 3000 USB webcam detected.\n");
++ name = "Logitech QuickCam Pro 3000";
+ type_id = 730;
+- break;
++ break;
++ case 0x08b1:
++ Info("Logitech QuickCam for Notebook Pro USB webcam detected.\n");
++ name = "Logitech QuickCam Notebook Pro";
++ type_id = 740; /* ?? unknown sensor */
++ break;
++ case 0x08b2:
++ Info("Logitech QuickCam 4000 Pro USB webcam detected.\n");
++ name = "Logitech QuickCam Pro 4000";
++ type_id = 740; /* CCD sensor */
++ break;
++ case 0x08b3:
++ Info("Logitech QuickCam Zoom USB webcam detected.\n");
++ name = "Logitech QuickCam Zoom";
++ type_id = 740; /* CCD sensor */
++ break;
+ default:
+ return NULL;
+ break;
+@@ -1868,6 +1901,8 @@
+
+ pdev->unplugged = 1;
+ if (pdev->vdev != NULL) {
++ Trace(TRACE_PROBE, "Unregistering video device.\n");
++ video_unregister_device(pdev->vdev);
+ if (pdev->vopen) {
+ Info("Disconnected while device/video is open!\n");
+
+@@ -1895,8 +1930,6 @@
+ }
+ else {
+ /* Normal disconnect; remove from available devices */
+- Trace(TRACE_PROBE, "Unregistering video device normally.\n");
+- video_unregister_device(pdev->vdev);
+ kfree(pdev->vdev);
+ pdev->vdev = NULL;
+ }
+@@ -1914,7 +1947,7 @@
+
+
+ /* *grunt* We have to do atoi ourselves :-( */
+-static int pwc_atoi(char *s)
++static int pwc_atoi(const char *s)
+ {
+ int k = 0;
+
+@@ -1959,7 +1992,7 @@
+ MODULE_PARM(dev_hint, "0-10s");
+ MODULE_PARM_DESC(dev_hint, "Device node hints");
+
+-MODULE_DESCRIPTION("Philips USB webcam driver");
++MODULE_DESCRIPTION("Philips USB & OEM webcam driver");
+ MODULE_AUTHOR("Nemosoft Unv. <nemosoft@smcc.demon.nl>");
+ MODULE_LICENSE("GPL");
+
+@@ -1969,7 +2002,7 @@
+ char *sizenames[PSZ_MAX] = { "sqcif", "qsif", "qcif", "sif", "cif", "vga" };
+
+ Info("Philips PCA645/646 + PCVC675/680/690 + PCVC730/740/750 webcam module version " PWC_VERSION " loaded.\n");
+- Info("Also supports the Askey VC010, Logitech Quickcam 3000 Pro, Samsung MPC-C10 and MPC-C30,\n");
++ Info("Also supports the Askey VC010, various Logitech Quickcams, Samsung MPC-C10 and MPC-C30,\n");
+ Info("the Creative WebCam 5, SOTEC Afina Eye and Visionite VCS-UC300 and VCS-UM100.\n");
+
+ if (fps) {
+@@ -2087,7 +2120,7 @@
+ device_hint[i].serial_number[k] = '\0';
+ }
+ }
+-#ifdef PWC_DEBUG
++#if PWC_DEBUG
+ Debug("device_hint[%d]:\n", i);
+ Debug(" type : %d\n", device_hint[i].type);
+ Debug(" serial# : %s\n", device_hint[i].serial_number);
+diff -urN linux-2.4.20.orig/drivers/usb/pwc-ioctl.h linux-2.4.20/drivers/usb/pwc-ioctl.h
+--- linux-2.4.20.orig/drivers/usb/pwc-ioctl.h Mon Jan 6 22:15:48 2003
++++ linux-2.4.20/drivers/usb/pwc-ioctl.h Mon Dec 16 22:19:15 2002
+@@ -18,12 +18,13 @@
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+-/* This is pwc-ioctl.h belonging to PWC 8.6 */
++/* This is pwc-ioctl.h belonging to PWC 8.10 */
+
+ /*
+ Changes
+ 2001/08/03 Alvarado Added ioctl constants to access methods for
+ changing white balance and red/blue gains
++ 2002/12/15 G. H. Fernandez-Toribio VIDIOCGREALSIZE
+ */
+
+ /* These are private ioctl() commands, specific for the Philips webcams.
+@@ -104,7 +105,12 @@
+ int led_off; /* Led off-time; range = 0..25000 */
+ };
+
+-
++/* Image size (used with GREALSIZE) */
++struct pwc_imagesize
++{
++ int width;
++ int height;
++};
+
+ /* Restore user settings */
+ #define VIDIOCPWCRUSER _IO('v', 192)
+@@ -173,4 +179,7 @@
+ #define VIDIOCPWCSDYNNOISE _IOW('v', 209, int)
+ #define VIDIOCPWCGDYNNOISE _IOR('v', 209, int)
+
++ /* Real image size as used by the camera; tells you whether or not there's a gray border around the image */
++#define VIDIOCPWCGREALSIZE _IOR('v', 210, struct pwc_imagesize)
++
+ #endif
+diff -urN linux-2.4.20.orig/drivers/usb/pwc-uncompress.c linux-2.4.20/drivers/usb/pwc-uncompress.c
+--- linux-2.4.20.orig/drivers/usb/pwc-uncompress.c Mon Jan 6 22:15:48 2003
++++ linux-2.4.20/drivers/usb/pwc-uncompress.c Mon Dec 16 22:19:15 2002
+@@ -77,7 +77,7 @@
+ {
+ struct pwc_frame_buf *fbuf;
+ int n, line, col, stride;
+- void *yuv, *image, *dst;
++ void *yuv, *image;
+ u16 *src;
+ u16 *dsty, *dstu, *dstv;
+
+@@ -114,19 +114,6 @@
+ to get the desired output format/size.
+ */
+ switch (pdev->vpalette) {
+- case VIDEO_PALETTE_YUV420:
+- /* Calculate byte offsets per line in image & view */
+- n = (pdev->image.x * 3) / 2;
+- col = (pdev->view.x * 3) / 2;
+- /* Offset into image */
+- dst = image + (pdev->view.x * pdev->offset.y + pdev->offset.x) * 3 / 2;
+- for (line = 0; line < pdev->image.y; line++) {
+- memcpy(dst, yuv, n);
+- yuv += n;
+- dst += col;
+- }
+- break;
+-
+ case VIDEO_PALETTE_YUV420P:
+ /*
+ * We do some byte shuffling here to go from the
+@@ -163,17 +150,20 @@
+ dstu += (stride >> 1);
+ }
+ break;
++ default:
++ Err("Unsupported palette!");
++ break;
+ }
+ }
+ else {
+ /* Compressed; the decompressor routines will write the data
+- in interlaced or planar format immediately.
++ in planar format immediately.
+ */
+ if (pdev->decompressor)
+ pdev->decompressor->decompress(
+ &pdev->image, &pdev->view, &pdev->offset,
+- yuv, image,
+- pdev->vpalette == VIDEO_PALETTE_YUV420P ? 1 : 0,
++ yuv, image,
++ 1,
+ pdev->decompress_data, pdev->vbandlength);
+ else
+ return -ENXIO; /* No such device or address: missing decompressor */
+diff -urN linux-2.4.20.orig/drivers/usb/pwc.h linux-2.4.20/drivers/usb/pwc.h
+--- linux-2.4.20.orig/drivers/usb/pwc.h Mon Jan 6 22:15:48 2003
++++ linux-2.4.20/drivers/usb/pwc.h Mon Dec 16 22:19:15 2002
+@@ -60,8 +60,8 @@
+
+ /* Version block */
+ #define PWC_MAJOR 8
+-#define PWC_MINOR 6
+-#define PWC_VERSION "8.6"
++#define PWC_MINOR 8
++#define PWC_VERSION "8.8"
+ #define PWC_NAME "pwc"
+
+ /* Turn certain features on/off */
+@@ -130,7 +130,7 @@
+ int vcinterface; /* video control interface */
+ int valternate; /* alternate interface needed */
+ int vframes, vsize; /* frames-per-second & size (see PSZ_*) */
+- int vpalette; /* YUV, RGB24, RGB32, etc */
++ int vpalette; /* YUV */
+ int vframe_count; /* received frames */
+ int vframes_dumped; /* counter for dumped frames */
+ int vframes_error; /* frames received in error */
+@@ -140,7 +140,8 @@
+ int vbandlength; /* compressed band length; 0 is uncompressed */
+ char vsnapshot; /* snapshot mode */
+ char vsync; /* used by isoc handler */
+-
++ char vmirror; /* for ToUCaM series */
++
+ /* The image acquisition requires 3 to 4 steps:
+ 1. data is gathered in short packets from the USB controller
+ 2. data is synchronized and packed into a frame buffer