ChangeSet 1.842.46.15, 2002/11/27 09:46:14-08:00, nemosoft@smcc.demon.nl

[PATCH] [PATCH] PWC 8.9

After a little absence, here's a patch to bring the Philips Webcam driver up
to version 8.9 (skipping 8.8 which has been available as a download on my
website for a while).

This patch is against 2.5.49, and includes some of the following:
* New USB IDs for Logitech and Visionite webcams.
* Better URB link/unlink sequence when opening/closing device
  and switching resolutions.
* Adding probe for CCD/CMOS sensor type.
* Removed remnants of YUV420 palette stuff.

Also updated the description in 'Kconfig'.


diff -Nru a/drivers/usb/media/Kconfig b/drivers/usb/media/Kconfig
--- a/drivers/usb/media/Kconfig	Wed Nov 27 12:49:43 2002
+++ b/drivers/usb/media/Kconfig	Wed Nov 27 12:49:43 2002
@@ -120,15 +120,26 @@
 	tristate "USB Philips Cameras"
 	depends on USB && VIDEO_DEV
 	---help---
-	  Say Y or M here if you want to use one of these Philips USB webcams:
-	  PCA645, PCA646, PCVC675, PCVC680, PCVC690, PCVC730, PCVC740, or
-	  the Askey VC010. The PCA635, PCVC665 and PCVC720 are not supported
-	  by this driver and never will be.
+	  Say Y or M here if you want to use one of these Philips & OEM
+          webcams:
+           * Philips PCA645, PCA646
+           * Philips PCVC675, PCVC680, PCVC690
+           * Philips PCVC730, PCVC740, PCVC750
+	   * Askey VC010
+	   * Logitech QuickCam Pro 3000, 4000, 'Zoom' and 'Notebook'
+	   * Samsung MPC-C10, MPC-C30
+	   * Creative Webcam 5
+	   * SOTECT Afina Eye
+	   * Visionite VCS-UC300, VCS-UM100
+	   
+	  The PCA635, PCVC665 and PCVC720 are not supported by this driver
+	  and never will be, but the 665 and 720 are supported by other 
+	  drivers.
 
-	  This driver has an optional plugin, which is distributed as a binary
-	  module only.  It contains code that allow you to use higher
-	  resolutions and framerates but may not be distributed as source.
-	  But even without this plugin you can these cams for most
+	  This driver has an optional plugin (called PWCX), which is 
+	  distributed as a binary module only. It contains code that allow you 
+	  to use higher resolutions and framerates but may not be distributed 
+	  as source. But even without this plugin you can these cams for most
 	  applications.
 
 	  See <file:Documentation/usb/philips.txt> for more information and
diff -Nru a/drivers/usb/media/pwc-ctrl.c b/drivers/usb/media/pwc-ctrl.c
--- a/drivers/usb/media/pwc-ctrl.c	Wed Nov 27 12:49:43 2002
+++ b/drivers/usb/media/pwc-ctrl.c	Wed Nov 27 12:49:43 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);
 		
@@ -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;
@@ -1279,7 +1270,6 @@
 	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;
 }
 
@@ -1363,12 +1353,10 @@
 
 	case VIDIOCPWCPROBE:
 	{
-		struct pwc_probe probe;
+		struct pwc_probe *probe = arg;
 		
-		strcpy(probe.name, pdev->vdev->name);
-		probe.type = pdev->type;
-		if (copy_to_user(arg, &probe, sizeof(probe)))
-			ret = -EFAULT;
+		strcpy(probe->name, pdev->vdev->name);
+		probe->type = pdev->type;
 		break;
 	}
 
diff -Nru a/drivers/usb/media/pwc-if.c b/drivers/usb/media/pwc-if.c
--- a/drivers/usb/media/pwc-if.c	Wed Nov 27 12:49:43 2002
+++ b/drivers/usb/media/pwc-if.c	Wed Nov 27 12:49:43 2002
@@ -1,6 +1,6 @@
 /* Linux driver for Philips webcam 
    USB and Video4Linux interface part.
-   (C) 1999-2001 Nemosoft Unv.
+   (C) 1999-2002 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
@@ -42,32 +42,31 @@
    - 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>
 #include <linux/vmalloc.h>
 #include <linux/wrapper.h>
-#include <linux/mm.h>
 #include <asm/io.h>
 
 #include "pwc.h"
 #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 */
 static struct usb_device_id pwc_device_table [] = {
-	{ USB_DEVICE(0x0471, 0x0302) },
+	{ USB_DEVICE(0x0471, 0x0302) }, /* Philips models */
 	{ USB_DEVICE(0x0471, 0x0303) },
 	{ USB_DEVICE(0x0471, 0x0304) },
 	{ USB_DEVICE(0x0471, 0x0307) },
@@ -76,12 +75,17 @@
 	{ USB_DEVICE(0x0471, 0x0310) },
 	{ USB_DEVICE(0x0471, 0x0311) },
 	{ USB_DEVICE(0x0471, 0x0312) },
-	{ USB_DEVICE(0x069A, 0x0001) },
-	{ USB_DEVICE(0x046D, 0x08b0) },
-	{ USB_DEVICE(0x055D, 0x9000) },
+	{ USB_DEVICE(0x069A, 0x0001) }, /* Askey */
+	{ USB_DEVICE(0x046D, 0x08b0) }, /* Logitech QuickCam Pro 3000 */
+	{ USB_DEVICE(0x046D, 0x08b1) }, /* Logitech QuickCam for Notebooks */
+	{ 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) },
-	{ USB_DEVICE(0x04CC, 0x8116) },
+	{ USB_DEVICE(0x041E, 0x400C) }, /* Creative Webcam 5 */
+	{ USB_DEVICE(0x04CC, 0x8116) }, /* Afina Eye */
+	{ USB_DEVICE(0x0d81, 0x1910) }, /* Visionite */
+	{ USB_DEVICE(0x0d81, 0x1900) },
 	{ }
 };
 MODULE_DEVICE_TABLE(usb, pwc_device_table);
@@ -106,7 +110,7 @@
 static int default_mbufs = 2;	/* Default number of mmap() buffers */
        int pwc_trace = TRACE_MODULE | TRACE_FLOW | TRACE_PWCX;
 static int power_save = 0;
-static int led_on = 1, led_off = 0; /* defaults to LED that is on while in use */
+static int led_on = 100, led_off = 0; /* defaults to LED that is on while in use */
        int pwc_preferred_compression = 2; /* 0..3 = uncompressed..high */
 static struct {
 	int type;
@@ -167,7 +171,7 @@
    succeeded. The pwc_device struct links back to both structures.
 
    When a device is unplugged while in use it will be removed from the 
-   list of known USB devices; I also de-register as a V4L device, but 
+   list of known USB devices; I also de-register it as a V4L device, but 
    unfortunately I can't free the memory since the struct is still in use
    by the file descriptor. This free-ing is then deferend until the first
    opportunity. Crude, but it works.
@@ -240,7 +244,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 +319,9 @@
 	for (; i < MAX_IMAGES; i++)
 		pdev->image_ptr[i] = NULL;
 
-	Trace(TRACE_MEMORY, "Leaving pwc_allocate_buffers().\n");
+	kbuf = NULL;
+	  
+	Trace(TRACE_MEMORY, "<< pwc_allocate_buffers()\n");
 	return 0;
 }
 
@@ -369,6 +375,7 @@
 		rvfree(pdev->image_data, default_mbufs * pdev->len_per_image);
 	}
 	pdev->image_data = NULL;
+	
 	Trace(TRACE_MEMORY, "Leaving free_buffers().\n");
 }
 
@@ -570,12 +577,10 @@
 	pdev->fill_image = (pdev->fill_image + 1) % default_mbufs;
 }
 
-/* 2001-10-14: The YUV420 is still there, but you can only set it from within 
-   a program (YUV420P being the default) */
+/* 2002-10-11: YUV420P is the only palette remaining. */
 static int pwc_set_palette(struct pwc_device *pdev, int pal)
 {
-	if (   pal == VIDEO_PALETTE_YUV420
-            || pal == VIDEO_PALETTE_YUV420P
+	if (   pal == VIDEO_PALETTE_YUV420P
 #if PWC_DEBUG
             || pal == VIDEO_PALETTE_RAW
 #endif
@@ -613,7 +618,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) {
@@ -686,9 +691,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.
@@ -705,7 +723,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 */
@@ -741,17 +759,23 @@
 			} /* .. 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)
 		wake_up_interruptible(&pdev->frameq);
 
 	urb->dev = pdev->udev;
-	usb_submit_urb(urb, GFP_ATOMIC);
+	i = usb_submit_urb(urb, GFP_ATOMIC);
+	if (i != 0)
+		Err("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i);
 }
 
 
@@ -762,7 +786,6 @@
 	int i, j, ret;
 
 	struct usb_host_interface *idesc;
-	int cur_alt;
 
 	if (pdev == NULL)
 		return -EFAULT;
@@ -770,12 +793,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;
 
@@ -792,7 +814,13 @@
 		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, GFP_KERNEL);
 		if (urb == NULL) {
@@ -801,6 +829,7 @@
 			break;
 		}
 		pdev->sbuf[i].urb = urb;
+		Trace(TRACE_MEMORY, "Allocated URB at 0x%p\n", urb);
 	}
 	if (ret) {
 		/* De-allocate in reverse order */
@@ -812,8 +841,7 @@
 		}
 		return ret;
 	}
-	
-	
+
 	/* init URB structure */	
 	for (i = 0; i < MAX_ISO_BUFS; i++) {
 		urb = pdev->sbuf[i].urb;
@@ -829,7 +857,7 @@
 		urb->start_frame = 0;
 		urb->number_of_packets = ISO_FRAMES_PER_DESC;
 		for (j = 0; j < ISO_FRAMES_PER_DESC; j++) {
-			urb->iso_frame_desc[j].offset = j * pdev->vmax_packet_size;
+			urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE;
 			urb->iso_frame_desc[j].length = pdev->vmax_packet_size;
 		}
 	}
@@ -840,11 +868,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;
 }
 
@@ -852,21 +881,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)
@@ -881,11 +923,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 transfer in pwc_try_video_mode.\n");
+	pdev->drop_frames++; /* try to avoid garbage during switch */
 	return ret;
 }
 
@@ -921,7 +962,7 @@
 	struct video_device *vdev = video_devdata(file);
 	struct pwc_device *pdev;
 
-	Trace(TRACE_OPEN, "video_open called(0x%p).\n", vdev);
+	Trace(TRACE_OPEN, ">> video_open called(vdev = 0x%p).\n", vdev);
 	
 	pdev = (struct pwc_device *)vdev->priv;
 	if (pdev == NULL)
@@ -932,10 +973,30 @@
 	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) {
+			/* Query CMOS sensor type */
+			const char *sensor_type = NULL;
+
+			i = pwc_get_cmos_sensor(pdev);
+			switch(i) {
+			case -1: /* Unknown, show nothing */; break;
+			case 0x00:  sensor_type = "Hyundai CMOS sensor"; break;
+			case 0x20:  sensor_type = "Sony CCD sensor + TDA8787"; break;
+			case 0x2E:  sensor_type = "Sony CCD sensor + Exas 98L59"; break;
+			case 0x2F:  sensor_type = "Sony CCD sensor + ADI 9804"; break;
+			case 0x30:  sensor_type = "Sharp CCD sensor + TDA8787"; break;
+			case 0x3E:  sensor_type = "Sharp CCD sensor + Exas 98L59"; break;
+			case 0x3F:  sensor_type = "Sharp CCD sensor + ADI 9804"; break;
+			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;
+			}
+			if (sensor_type != NULL)
+				Info("This %s camera is equipped with a %s (%d).\n", pdev->vdev->name, sensor_type, i);
+		}
 	}
 
 	/* Turn on camera */
@@ -1000,12 +1061,6 @@
 		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);
@@ -1023,7 +1078,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;
 }
 
@@ -1034,15 +1089,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) 
@@ -1050,15 +1102,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) {
-		/* 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) 
@@ -1077,6 +1128,7 @@
 	if (pdev->unplugged)
 		wake_up(&pdev->remove_ok);
 	file->private_data = NULL;
+	Trace(TRACE_OPEN, "<< video_close()\n");
 	return 0;
 }
 
@@ -1514,7 +1566,6 @@
 	Trace(TRACE_MEMORY, "mmap(0x%p, 0x%lx, %lu) called.\n", vdev, start, size);
 	pdev = vdev->priv;
 
-	/* FIXME - audit mmap during a read */		
 	pos = (unsigned long)pdev->image_data;
 	while (size > 0) {
 		page = kvirt_to_pa(pos);
@@ -1547,14 +1598,14 @@
 	int vendor_id, product_id, type_id;
 	int i, hint;
 	int video_nr = -1; /* default: use next available device */
-	char serial_number[30];
+	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,
-			intf->altsetting->desc.bInterfaceNumber);
+	Trace(TRACE_PROBE, "probe() called [%04X %04X], if %d\n", 
+		udev->descriptor.idVendor, udev->descriptor.idProduct, 
+		intf->altsetting->desc.bInterfaceNumber);
 
 	/* the interfaces are probed one by one. We are only interested in the
 	   video interface (0) now.
@@ -1570,38 +1621,47 @@
 		switch (product_id) {
 		case 0x0302:
 			Info("Philips PCA645VC USB webcam detected.\n");
+			name = "Philips 645 webcam";
 			type_id = 645;
 			break;
 		case 0x0303:
 			Info("Philips PCA646VC USB webcam detected.\n");
+			name = "Philips 646 webcam";
 			type_id = 646;
 			break;
 		case 0x0304:
 			Info("Askey VC010 type 2 USB webcam detected.\n");
+			name = "Askey VC010 webcam";
 			type_id = 646;
 			break;
 		case 0x0307:
 			Info("Philips PCVC675K (Vesta) USB webcam detected.\n");
+			name = "Philips 675 webcam";
 			type_id = 675;
 			break;
 		case 0x0308:
 			Info("Philips PCVC680K (Vesta Pro) USB webcam detected.\n");
+			name = "Philips 680 webcam";
 			type_id = 680;
 			break;
 		case 0x030C:
 			Info("Philips PCVC690K (Vesta Pro Scan) USB webcam detected.\n");
+			name = "Philips 690 webcam";
 			type_id = 690;
 			break;
 		case 0x0310:
 			Info("Philips PCVC730K (ToUCam Fun) USB webcam detected.\n");
+			name = "Philips 730 webcam";
 			type_id = 730;
 			break;
 		case 0x0311:
 			Info("Philips PCVC740K (ToUCam Pro) USB webcam detected.\n");
+			name = "Philips 740 webcam";
 			type_id = 740;
 			break;
 		case 0x0312:
 			Info("Philips PCVC750K (ToUCam Pro Scan) USB webcam detected.\n");
+			name = "Philips 750 webcam";
 			type_id = 750;
 			break;
 		default:
@@ -1613,6 +1673,7 @@
 		switch(product_id) {
 		case 0x0001:
 			Info("Askey VC010 type 1 USB webcam detected.\n");
+			name = "Askey VC010 webcam";
 			type_id = 645;
 			break;
 		default:
@@ -1623,9 +1684,25 @@
 	else if (vendor_id == 0x046d) {
 		switch(product_id) {
 		case 0x08b0:
-			Info("Logitech QuickCam 3000 Pro detected.\n");
+			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 Noteboos USB webcam detected.\n");
+			name = "Logitech QuickCam Notebook";
+			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; /* ?? unknown sensor */
+			break;
         	default:
 			return -ENODEV;
         		break;
@@ -1639,10 +1716,12 @@
 		switch(product_id) {
 		case 0x9000:
 			Info("Samsung MPC-C10 USB webcam detected.\n");
+			name = "Samsung MPC-C10";
 			type_id = 675;
 			break;
 		case 0x9001:
 			Info("Samsung MPC-C30 USB webcam detected.\n");
+			name = "Samsung MPC-C30";
 			type_id = 675;
 			break;
 		default:
@@ -1654,6 +1733,7 @@
 		switch(product_id) {
 		case 0x400c:
 			Info("Creative Labs Webcam 5 detected.\n");
+			name = "Creative Labs Webcam 5";
 			type_id = 730;
 			break;
 		default:
@@ -1664,7 +1744,8 @@
 	else if (vendor_id == 0x04cc) { 
 		switch(product_id) {
 		case 0x8116:
-			Info("SOTEC CMS-001 USB webcam detected.\n");
+			Info("Sotec Afina Eye USB webcam detected.\n");
+			name = "Sotec Afina Eye";
 			type_id = 730;
 			break;  
 		default:
@@ -1672,7 +1753,25 @@
 			break;
 		}
 	}
-	else return -ENODEV; /* Not Philips, Askey, Logitech, Samsung, Creative or SOTEC, for sure. */
+	else if (vendor_id == 0x0d81) {
+		switch(product_id) {
+		case 0x1900:
+			Info("Visionite VCS-UC300 USB webcam detected.\n");
+			name = "Visionite VCS-UC300";
+			type_id = 740; /* CCD sensor */
+			break;
+		case 0x1910:
+			Info("Visionite VCS-UM100 USB webcam detected.\n");
+			name = "Visionite VCS-UM100";
+			type_id = 730; /* CMOS sensor */
+			break;
+		default:
+			return -ENODEV;
+			break;
+		}
+	}
+	else 
+		return -ENODEV; /* Not any of the know types; but the list keeps growing. */
 
 	memset(serial_number, 0, 30);
 	usb_string(udev, udev->descriptor.iSerialNumber, serial_number, 29);
@@ -1706,7 +1805,7 @@
 		return -ENOMEM;
 	}
 	memcpy(vdev, &pwc_template, sizeof(pwc_template));
-	sprintf(vdev->name, "Philips %d webcam", pdev->type);
+	strcpy(vdev->name, name);
 	SET_MODULE_OWNER(vdev);
 	pdev->vdev = vdev;
 	vdev->priv = pdev;
@@ -1714,7 +1813,6 @@
 	pdev->release = udev->descriptor.bcdDevice;
 	Trace(TRACE_PROBE, "Release: %04x\n", pdev->release);
 
-
 	/* Now search device_hint[] table for a match, so we can hint a node number. */
 	for (hint = 0; hint < MAX_DEV_HINTS; hint++) {
 		if (((device_hint[hint].type == -1) || (device_hint[hint].type == pdev->type)) &&
@@ -1761,40 +1859,41 @@
 	dev_set_drvdata (&intf->dev, NULL);
 	if (pdev == NULL) {
 		Err("pwc_disconnect() Called without private pointer.\n");
-		goto out_err;
+		goto disconnect_out;
 	}
 	if (pdev->udev == NULL) {
 		Err("pwc_disconnect() already called for %p\n", pdev);
-		goto out_err;
+		goto disconnect_out;
 	}
 	if (pdev->udev != interface_to_usbdev(intf)) {
 		Err("pwc_disconnect() Woops: pointer mismatch udev/pdev.\n");
-		goto out_err;
+		goto disconnect_out;
 	}
 #ifdef PWC_MAGIC	
 	if (pdev->magic != PWC_MAGIC) {
 		Err("pwc_disconnect() Magic number failed. Consult your scrolls and try again.\n");
-		goto out_err;
+		goto disconnect_out;
 	}
-#endif
-
+#endif	
+	
 	pdev->unplugged = 1;
 	if (pdev->vdev != NULL) {
-		video_unregister_device(pdev->vdev);
+		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.
+			   stuff here, but we have to wait until close() 
+			   is finished. 
 			 */
-
+			   
 			Trace(TRACE_PROBE, "Sleeping on remove_ok.\n");
 			add_wait_queue(&pdev->remove_ok, &wait);
 			set_current_state(TASK_UNINTERRUPTIBLE);
@@ -1808,26 +1907,25 @@
 		}
 		else {
 			/* Normal disconnect; remove from available devices */
-			Trace(TRACE_PROBE, "Unregistering video device normally.\n");
 			kfree(pdev->vdev);
 			pdev->vdev = NULL;
 		}
 	}
 
+disconnect_out:
 	/* 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;
-out_err:
 	unlock_kernel();
 	kfree(pdev);
 }
 
 
 /* *grunt* We have to do atoi ourselves :-( */
-static int pwc_atoi(char *s)
+static int pwc_atoi(const char *s)
 {
 	int k = 0;
 	
@@ -1872,7 +1970,7 @@
 MODULE_PARM(dev_hint, "0-10s");
 MODULE_PARM_DESC(dev_hint, "Device node hints");
 
-MODULE_DESCRIPTION("Philips USB webcam driver");
+MODULE_DESCRIPTION("Philips & OEM USB webcam driver");
 MODULE_AUTHOR("Nemosoft Unv. <nemosoft@smcc.demon.nl>");
 MODULE_LICENSE("GPL");
 
@@ -1882,11 +1980,12 @@
 	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, the Creative WebCam 5 and the SOTEC CMS-001.\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) {
-		if (fps < 5 || fps > 30) {
-			Err("Framerate out of bounds (5-30).\n");
+		if (fps < 4 || fps > 30) {
+			Err("Framerate out of bounds (4-30).\n");
 			return -EINVAL;
 		}
 		default_fps = fps;
@@ -1938,9 +2037,9 @@
 	if (power_save)
 		Info("Enabling power save on open/close.\n");
 	if (leds[0] >= 0)
-		led_on = leds[0] / 100;
+		led_on = leds[0];
 	if (leds[1] >= 0)
-		led_off = leds[1] / 100;
+		led_off = leds[1];
 
 	/* Big device node whoopla. Basicly, it allows you to assign a 
 	   device node (/dev/videoX) to a camera, based on its type 
@@ -1999,7 +2098,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 -Nru a/drivers/usb/media/pwc-misc.c b/drivers/usb/media/pwc-misc.c
--- a/drivers/usb/media/pwc-misc.c	Wed Nov 27 12:49:43 2002
+++ b/drivers/usb/media/pwc-misc.c	Wed Nov 27 12:49:43 2002
@@ -1,6 +1,6 @@
 /* Linux driver for Philips webcam 
    Various miscellaneous functions and tables.
-   (C) 1999-2001 Nemosoft Unv. (webcam@smcc.demon.nl)
+   (C) 1999-2002 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/media/pwc-uncompress.c b/drivers/usb/media/pwc-uncompress.c
--- a/drivers/usb/media/pwc-uncompress.c	Wed Nov 27 12:49:43 2002
+++ b/drivers/usb/media/pwc-uncompress.c	Wed Nov 27 12:49:43 2002
@@ -1,6 +1,6 @@
 /* Linux driver for Philips webcam 
    Decompression frontend.
-   (C) 1999-2001 Nemosoft Unv. (webcam@smcc.demon.nl)
+   (C) 1999-2002 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
@@ -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 -Nru a/drivers/usb/media/pwc-uncompress.h b/drivers/usb/media/pwc-uncompress.h
--- a/drivers/usb/media/pwc-uncompress.h	Wed Nov 27 12:49:43 2002
+++ b/drivers/usb/media/pwc-uncompress.h	Wed Nov 27 12:49:43 2002
@@ -1,4 +1,4 @@
-/* (C) 1999-2001 Nemosoft Unv. (webcam@smcc.demon.nl)
+/* (C) 1999-2002 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/media/pwc.h b/drivers/usb/media/pwc.h
--- a/drivers/usb/media/pwc.h	Wed Nov 27 12:49:43 2002
+++ b/drivers/usb/media/pwc.h	Wed Nov 27 12:49:43 2002
@@ -60,8 +60,8 @@
 
 /* Version block */
 #define PWC_MAJOR	8
-#define PWC_MINOR	7
-#define PWC_VERSION 	"8.7"
+#define PWC_MINOR	9
+#define PWC_VERSION 	"8.9"
 #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
