mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH/RFC 2.6.17-rc4 1/1] usbvideo/quickcam_messenger driver v4
@ 2006-05-17  2:36 jayakumar.video
  2006-05-17  6:14 ` [linux-usb-devel] " Jaya Kumar
  0 siblings, 1 reply; 5+ messages in thread
From: jayakumar.video @ 2006-05-17  2:36 UTC (permalink / raw)
  To: linux-kernel, linux-usb-devel

[ sorry, bungled the l-u-devel list address and subject in prev email ]

Hi Greg KH, USB folk, kernel folk,

Appended is v4 of my patch adding a usbvideo driver for the Logitech 
Quickcam Messenger USB webcam. This patch has been rebased against 
2.6.17-rc4 as per Greg's feedback. 

Please let me know if it looks okay and if you have any feedback
or suggestions. 

Thanks,
Jaya Kumar

Signed-off-by: Jaya Kumar <jayakumar.video@gmail.com>

---

 Kconfig              |   12 
 Makefile             |    1 
 quickcam_messenger.c | 1120 +++++++++++++++++++++++++++++++++++++++++++++++++++
 quickcam_messenger.h |  126 +++++
 4 files changed, 1259 insertions(+)

---

diff -X linux-2.6.17-rc4/Documentation/dontdiff -uprN linux-2.6.17-rc4-vanilla/drivers/media/video/usbvideo/Kconfig linux-2.6.17-rc4/drivers/media/video/usbvideo/Kconfig
--- linux-2.6.17-rc4-vanilla/drivers/media/video/usbvideo/Kconfig	2006-05-17 08:11:36.000000000 +0800
+++ linux-2.6.17-rc4/drivers/media/video/usbvideo/Kconfig	2006-05-17 08:39:51.674492008 +0800
@@ -36,3 +36,15 @@ config USB_KONICAWC
 
 	  To compile this driver as a module, choose M here: the
 	  module will be called konicawc.
+
+config USB_QUICKCAM_MESSENGER
+	tristate "USB Logitech Quickcam Messenger"
+	depends on USB && VIDEO_DEV
+	select VIDEO_USBVIDEO
+	---help---
+	  Say Y or M here to enable support for the USB Logitech Quickcam
+          Messenger webcam.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called quickcam_messenger.
+
diff -X linux-2.6.17-rc4/Documentation/dontdiff -uprN linux-2.6.17-rc4-vanilla/drivers/media/video/usbvideo/Makefile linux-2.6.17-rc4/drivers/media/video/usbvideo/Makefile
--- linux-2.6.17-rc4-vanilla/drivers/media/video/usbvideo/Makefile	2006-05-17 08:11:36.000000000 +0800
+++ linux-2.6.17-rc4/drivers/media/video/usbvideo/Makefile	2006-05-17 08:40:14.000000000 +0800
@@ -2,3 +2,4 @@ obj-$(CONFIG_VIDEO_USBVIDEO)    += usbvi
 obj-$(CONFIG_USB_IBMCAM)        += ibmcam.o ultracam.o
 obj-$(CONFIG_USB_KONICAWC)      += konicawc.o
 obj-$(CONFIG_USB_VICAM)         += vicam.o
+obj-$(CONFIG_USB_QUICKCAM_MESSENGER)	+= quickcam_messenger.o 
diff -X linux-2.6.17-rc4/Documentation/dontdiff -uprN linux-2.6.17-rc4-vanilla/drivers/media/video/usbvideo/quickcam_messenger.c linux-2.6.17-rc4/drivers/media/video/usbvideo/quickcam_messenger.c
--- linux-2.6.17-rc4-vanilla/drivers/media/video/usbvideo/quickcam_messenger.c	1970-01-01 07:30:00.000000000 +0730
+++ linux-2.6.17-rc4/drivers/media/video/usbvideo/quickcam_messenger.c	2006-05-17 08:37:05.000000000 +0800
@@ -0,0 +1,1120 @@
+/*
+ * Driver for Logitech Quickcam Messenger usb video camera 
+ * Copyright (C) Jaya Kumar
+ *  
+ * This work was sponsored by CIS(M) Sdn Bhd.
+ * History:
+ * 05/08/2006 - Jaya Kumar
+ * I wrote this based on the konicawc by Simon Evans. 
+ * -
+ * Full credit for reverse engineering and creating an initial
+ * working linux driver for the VV6422 goes to the qce-ga project by
+ * Tuukka Toivonen, Jochen Hoenicke, Peter McConnell, 
+ * Cristiano De Michele, Georg Acher, Jean-Frederic Clere as well as
+ * others. 
+ * ---
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/usb_input.h>
+
+#include "usbvideo.h"
+#include "quickcam_messenger.h"
+
+/*
+ * Version Information
+ */
+
+#ifdef CONFIG_USB_DEBUG
+static int debug;
+#define DEBUG(n, format, arg...) \
+	if (n <= debug) {	 \
+		printk(KERN_DEBUG __FILE__ ":%s(): " format "\n", __FUNCTION__ , ## arg); \
+	}
+#else
+#define DEBUG(n, arg...)
+static const int debug = 0;
+#endif
+
+#define DRIVER_VERSION "v0.01"
+#define DRIVER_DESC "Logitech Quickcam Messenger USB"
+
+#define USB_LOGITECH_VENDOR_ID	0x046D
+#define USB_QCM_PRODUCT_ID	0x08F0
+
+#define MAX_CAMERAS	1
+
+#define MAX_COLOUR	32768
+#define MAX_HUE		32768
+#define MAX_BRIGHTNESS	32768
+#define MAX_CONTRAST	32768
+#define MAX_WHITENESS	32768
+
+static int size = SIZE_320X240;
+static int colour = MAX_COLOUR;
+static int hue = MAX_HUE;
+static int brightness =	MAX_BRIGHTNESS;
+static int contrast =	MAX_CONTRAST;
+static int whiteness =	MAX_WHITENESS;
+
+static struct usbvideo *cams;
+
+static struct usb_device_id qcm_table [] = {
+	{ USB_DEVICE(USB_LOGITECH_VENDOR_ID, USB_QCM_PRODUCT_ID) },
+	{ }
+};
+MODULE_DEVICE_TABLE(usb, qcm_table);
+
+#ifdef CONFIG_INPUT
+static void qcm_register_input(struct qcm *cam, struct usb_device *dev)
+{
+	struct input_dev *input_dev;
+
+	usb_make_path(dev, cam->input_physname, sizeof(cam->input_physname));
+	strncat(cam->input_physname, "/input0", sizeof(cam->input_physname));
+
+	cam->input = input_dev = input_allocate_device();
+	if (!input_dev) {
+		warn("insufficient mem for cam input device");
+		return;
+	}
+
+	input_dev->name = "QCM button"; 
+	input_dev->phys = cam->input_physname;
+	usb_to_input_id(dev, &input_dev->id);
+	input_dev->cdev.dev = &dev->dev;
+
+	input_dev->evbit[0] = BIT(EV_KEY);
+	input_dev->keybit[LONG(BTN_0)] = BIT(BTN_0);
+	
+	input_dev->private = cam;
+	
+	input_register_device(cam->input);
+}
+
+static void qcm_unregister_input(struct qcm *cam) 
+{
+	if (cam->input) {
+		input_unregister_device(cam->input);
+		cam->input = NULL;
+	} 
+}
+
+static void qcm_report_buttonstat(struct qcm *cam) 
+{ 
+	if (cam->input) {
+		input_report_key(cam->input, BTN_0, cam->button_sts);
+		input_sync(cam->input);
+	}
+}
+
+static void qcm_int_irq(struct urb *urb, struct pt_regs *regs)
+{
+	int ret;
+	struct uvd *uvd = urb->context;
+	struct qcm *cam; 
+
+	if (!CAMERA_IS_OPERATIONAL(uvd))
+		return;
+
+	if (!uvd->streaming) 
+		return;
+
+	uvd->stats.urb_count++;
+
+	if (urb->status < 0) 
+		uvd->stats.iso_err_count++;
+	else {
+		if (urb->actual_length > 0 ) {
+			cam = (struct qcm *) uvd->user_data;
+			if (cam->button_sts_buf == 0x88)
+				cam->button_sts = 0x0;
+			else if (cam->button_sts_buf == 0x80)
+				cam->button_sts = 0x1;
+			qcm_report_buttonstat(cam);
+		}
+	}
+
+	ret = usb_submit_urb(urb, GFP_ATOMIC);
+	if (ret < 0) 
+		err("usb_submit_urb error (%d)", ret);
+}
+
+static int qcm_setup_input_int(struct qcm *cam, struct uvd *uvd)
+{
+	int errflag;	
+	usb_fill_int_urb(cam->button_urb, uvd->dev, 
+			usb_rcvintpipe(uvd->dev, uvd->video_endp + 1),
+			&cam->button_sts_buf,
+			1,
+			qcm_int_irq,
+			uvd, 16);
+
+	errflag = usb_submit_urb(cam->button_urb, GFP_KERNEL);
+	if (errflag)
+		err ("usb_submit_int ret %d", errflag);
+	return errflag;
+}
+
+static void qcm_stop_int_data(struct qcm *cam)
+{
+	usb_kill_urb(cam->button_urb);
+}
+
+static int qcm_alloc_int_urb(struct qcm *cam)
+{	
+	cam->button_urb = usb_alloc_urb(0, GFP_KERNEL);
+
+	if (!cam->button_urb)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void qcm_free_int(struct qcm *cam)
+{
+	if (cam->button_urb)
+		usb_free_urb(cam->button_urb);
+}
+#endif /* CONFIG_INPUT */
+
+static int qcm_stv_setb(struct usb_device *dev, u16 reg, u8 val)
+{
+	int ret;
+
+	/* we'll wait up to 3 slices but no more */
+	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+		0x04, USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
+		reg, 0, &val, 1, 3*HZ);
+	return ret;
+}
+
+static int qcm_stv_setw(struct usb_device *dev, u16 reg, u16 val)
+{
+	int ret;
+
+	/* we'll wait up to 3 slices but no more */
+	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+		0x04, USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
+		reg, 0, &val, 2, 3*HZ);
+	return ret;
+}
+
+static int qcm_stv_getw(struct usb_device *dev, unsigned short reg, 
+							__le16 *val)
+{
+	int ret;
+
+	/* we'll wait up to 3 slices but no more */
+	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+		0x04, USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE,
+		reg, 0, val, 2, 3*HZ);
+	return ret;
+}
+
+static int qcm_camera_on(struct uvd *uvd)
+{
+	int ret;
+	CHECK_RET(ret, qcm_stv_setb(uvd->dev, STV_ISO_ENABLE, 0x01));
+	return 0;
+}
+
+static int qcm_camera_off(struct uvd *uvd)
+{
+	int ret;
+	CHECK_RET(ret, qcm_stv_setb(uvd->dev, STV_ISO_ENABLE, 0x00));
+	return 0;
+}
+
+static void qcm_hsv2rgb(u16 hue, u16 sat, u16 val, u16 *r, u16 *g, u16 *b)
+{
+	unsigned int segment, valsat;
+	signed int   h = (signed int) hue;
+	unsigned int s = (sat - 32768) * 2;	/* rescale */
+	unsigned int v = val;	
+	unsigned int p;
+
+	/*
+	the registers controling gain are 8 bit of which
+	we affect only the last 4 bits with our gain.
+	we know that if saturation is 0, (unsaturated) then 
+	we're grayscale (center axis of the colour cone) so
+	we set rgb=value. we use a formula obtained from
+	wikipedia to map the cone to the RGB plane. it's
+	as follows for the human value case of h=0..360,
+	s=0..1, v=0..1
+	h_i = h/60 % 6 , f = h/60 - h_i , p = v(1-s)
+	q = v(1 - f*s) , t = v(1 - (1-f)s)
+	h_i==0 => r=v , g=t, b=p
+	h_i==1 => r=q , g=v, b=p
+	h_i==2 => r=p , g=v, b=t
+	h_i==3 => r=p , g=q, b=v
+	h_i==4 => r=t , g=p, b=v
+	h_i==5 => r=v , g=p, b=q
+	the bottom side (the point) and the stuff just up
+	of that is black so we simplify those two cases.
+	*/
+	if (sat < 32768) { 
+		/* anything less than this is unsaturated */
+		*r = val; 
+		*g = val; 
+		*b = val;
+		return;
+	}
+	if (val <= (0xFFFF/8)) {
+		/* anything less than this is black */
+		*r = 0;
+		*g = 0;
+		*b = 0;
+		return;
+	}
+
+	/* the rest of this code is copying tukkat's
+	implementation of the hsv2rgb conversion as taken
+	from qc-usb-messenger code. the 10923 is 0xFFFF/6
+	to divide the cone into 6 sectors.  */
+
+	segment = (h + 10923) & 0xFFFF;		
+	segment = segment*3 >> 16;		/* 0..2: 0=R, 1=G, 2=B */
+	hue -= segment * 21845;			/* -10923..10923 */
+	h = hue;
+	h *= 3;
+	valsat = v*s >> 16;			/* 0..65534 */
+	p = v - valsat;
+	if (h >= 0) {
+		unsigned int t = v - (valsat * (32769 - h) >> 15);
+		switch (segment) {
+		case 0:	/* R-> */
+			*r = v;
+			*g = t;
+			*b = p;
+			break;
+		case 1:	/* G-> */
+			*r = p;
+			*g = v;
+			*b = t;
+			break;
+		case 2:	/* B-> */
+			*r = t;
+			*g = p;
+			*b = v;
+			break;
+		}
+	} else {
+		unsigned int q = v - (valsat * (32769 + h) >> 15);
+		switch (segment) {
+		case 0:	/* ->R */
+			*r = v;
+			*g = p;
+			*b = q;
+			break;
+		case 1:	/* ->G */
+			*r = q;
+			*g = v;
+			*b = p;
+			break;
+		case 2:	/* ->B */
+			*r = p;
+			*g = q;
+			*b = v;
+			break;
+		}
+	}
+}
+
+static int qcm_sensor_set_gains(struct uvd *uvd, u16 hue, 
+	u16 saturation, u16 value)
+{
+	int ret;
+	u16 r,g,b;
+
+	/* this code is based on qc-usb-messenger */
+	qcm_hsv2rgb(hue, saturation, value, &r, &g, &b);
+
+	r >>= 12;
+	g >>= 12;
+	b >>= 12;
+
+	/* min val is 8 */
+	r = max((u16) 8, r);
+	g = max((u16) 8, g);
+	b = max((u16) 8, b);
+
+	r |= 0x30;
+	g |= 0x30;
+	b |= 0x30;
+
+	/* set the r,g,b gain registers */
+	CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x0509, r)); 
+	CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x050A, g));
+	CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x050B, b));
+
+	/* doing as qc-usb did */
+	CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x050C, 0x2A));
+	CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x050D, 0x01));
+	CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143F, 0x01));
+
+	return 0;
+}
+
+static int qcm_sensor_set_exposure(struct uvd *uvd, int exposure)
+{
+	int ret;
+	int formedval;
+
+	/* calculation was from qc-usb-messenger driver */
+	formedval = ( exposure >> 12 );
+
+	/* max value for formedval is 14 */
+	formedval = min(formedval, 14);
+
+	CHECK_RET(ret, qcm_stv_setb(uvd->dev, 
+			0x143A, 0xF0 | formedval));
+	CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143F, 0x01));
+	return 0;
+}
+
+static int qcm_sensor_setlevels(struct uvd *uvd, int brightness, int contrast,
+					int hue, int colour)
+{
+	int ret;
+	/* brightness is exposure, contrast is gain, colour is saturation */
+	CHECK_RET(ret, 
+		qcm_sensor_set_exposure(uvd, brightness));
+	CHECK_RET(ret, qcm_sensor_set_gains(uvd, hue, colour, contrast));
+
+	return 0;
+}
+
+static int qcm_sensor_setsize(struct uvd *uvd, u8 size)
+{
+	int ret;
+
+	CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x1505, size));
+	return 0;
+}
+
+static int qcm_sensor_set_shutter(struct uvd *uvd, int whiteness)
+{
+	int ret;
+	/* some rescaling as done by the qc-usb-messenger code */
+	if (whiteness > 0xC000) 
+		whiteness = 0xC000 + (whiteness & 0x3FFF)*8;
+		
+	CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143D, 
+				(whiteness >> 8) & 0xFF));
+	CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143E, 
+				(whiteness >> 16) & 0x03));
+	CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143F, 0x01));
+
+	return 0;
+}
+
+static int qcm_sensor_init(struct uvd *uvd)
+{
+	struct qcm *cam = (struct qcm *) uvd->user_data;
+	int ret;
+	int i;
+
+	for (i=0; i < sizeof(regval_table)/sizeof(regval_table[0]) ; i++) {
+		CHECK_RET(ret, qcm_stv_setb(uvd->dev, 
+					regval_table[i].reg, 
+					regval_table[i].val));
+	}
+
+	CHECK_RET(ret, qcm_stv_setw(uvd->dev, 0x15c1, 
+				cpu_to_le16(ISOC_PACKET_SIZE)));
+	CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x15c3, 0x08));
+	CHECK_RET(ret, ret = qcm_stv_setb(uvd->dev, 0x143f, 0x01));
+
+	CHECK_RET(ret, qcm_stv_setb(uvd->dev, STV_ISO_ENABLE, 0x00));
+
+	CHECK_RET(ret, qcm_sensor_setsize(uvd, camera_sizes[cam->size].cmd));
+
+	CHECK_RET(ret, qcm_sensor_setlevels(uvd, uvd->vpic.brightness, 
+			uvd->vpic.contrast, uvd->vpic.hue, uvd->vpic.colour));
+	
+	CHECK_RET(ret, qcm_sensor_set_shutter(uvd, uvd->vpic.whiteness));
+	CHECK_RET(ret, qcm_sensor_setsize(uvd, camera_sizes[cam->size].cmd));
+
+	return 0;
+}
+
+static int qcm_set_camera_size(struct uvd *uvd)
+{
+	int ret;
+	struct qcm *cam = (struct qcm *) uvd->user_data;
+
+	CHECK_RET(ret, qcm_sensor_setsize(uvd, camera_sizes[cam->size].cmd));
+	cam->width = camera_sizes[cam->size].width;
+	cam->height = camera_sizes[cam->size].height;
+	uvd->videosize = VIDEOSIZE(cam->width, cam->height);
+
+	return 0;
+}
+
+static int qcm_setup_on_open(struct uvd *uvd)
+{
+	int ret;
+
+	CHECK_RET(ret, qcm_sensor_set_gains(uvd, uvd->vpic.hue, 
+				uvd->vpic.colour, uvd->vpic.contrast));
+	CHECK_RET(ret, qcm_sensor_set_exposure(uvd, uvd->vpic.brightness));
+	CHECK_RET(ret, qcm_sensor_set_shutter(uvd, uvd->vpic.whiteness));
+	CHECK_RET(ret, qcm_set_camera_size(uvd));
+	CHECK_RET(ret, qcm_camera_on(uvd));
+	return 0;
+}
+
+static void qcm_adjust_picture(struct uvd *uvd)
+{
+	int ret;
+	struct qcm *cam = (struct qcm *) uvd->user_data;
+
+	ret = qcm_camera_off(uvd);
+	if (ret) {
+		err("can't turn camera off. abandoning pic adjustment");
+		return;
+	}
+
+	/* if there's been a change in contrast, hue, or 
+	colour then we need to recalculate hsv in order 
+	to update gains */
+	if ((cam->contrast != uvd->vpic.contrast) || 
+		(cam->hue != uvd->vpic.hue) ||
+		(cam->colour != uvd->vpic.colour)) { 
+		cam->contrast = uvd->vpic.contrast;
+		cam->hue = uvd->vpic.hue;
+		cam->colour = uvd->vpic.colour;
+		ret = qcm_sensor_set_gains(uvd, cam->hue, cam->colour, 
+						cam->contrast);
+		if (ret) {
+			err("can't set gains. abandoning pic adjustment");
+			return;
+		}
+	}	
+
+	if (cam->brightness != uvd->vpic.brightness) {
+		cam->brightness = uvd->vpic.brightness;
+		ret = qcm_sensor_set_exposure(uvd, cam->brightness);
+		if (ret) {
+			err("can't set exposure. abandoning pic adjustment");
+			return;
+		}
+	}
+
+	if (cam->whiteness != uvd->vpic.whiteness) {
+		cam->whiteness = uvd->vpic.whiteness;
+		qcm_sensor_set_shutter(uvd, cam->whiteness);
+		if (ret) {
+			err("can't set shutter. abandoning pic adjustment");
+			return;
+		}
+	}
+
+	ret = qcm_camera_on(uvd);
+	if (ret) {
+		err("can't reenable camera. pic adjustment failed");
+		return;
+	}
+}
+
+static int qcm_process_frame(struct uvd *uvd, u8 *cdata, int framelen)
+{
+	int datalen;
+	int totaldata;
+	struct framehdr {
+		__be16 id;
+		__be16 len;
+	};
+	struct framehdr *fhdr;
+
+	totaldata = 0;
+	while (framelen) {
+		fhdr = (struct framehdr *) cdata;
+		datalen = be16_to_cpu(fhdr->len);
+		framelen -= 4;
+		cdata += 4;
+
+		if ((fhdr->id) == cpu_to_be16(0x8001)) {
+			RingQueue_Enqueue(&uvd->dp, marker, 4);
+			totaldata += 4;
+			continue;
+		}
+		if ((fhdr->id & cpu_to_be16(0xFF00)) == cpu_to_be16(0x0200)) {
+			RingQueue_Enqueue(&uvd->dp, cdata, datalen);
+			totaldata += datalen;
+		}
+		framelen -= datalen;
+		cdata += datalen;
+	}
+	return totaldata;
+}
+
+static int qcm_compress_iso(struct uvd *uvd, struct urb *dataurb)
+{
+	int totlen;
+	int i;
+	unsigned char *cdata;
+
+	totlen=0;
+	for (i = 0; i < dataurb->number_of_packets; i++) {
+		int n = dataurb->iso_frame_desc[i].actual_length;
+		int st = dataurb->iso_frame_desc[i].status;
+
+		cdata = dataurb->transfer_buffer +
+			dataurb->iso_frame_desc[i].offset;
+
+		if (st < 0) {
+			warn("Data error: packet=%d. len=%d. status=%d.",
+			      i, n, st);
+			uvd->stats.iso_err_count++;
+			continue;
+		}
+		if (!n) 
+			continue;
+
+		totlen += qcm_process_frame(uvd, cdata, n);
+	}
+	return totlen;
+}
+
+static void resubmit_urb(struct uvd *uvd, struct urb *urb)
+{
+	int ret;
+	
+	urb->dev = uvd->dev;
+	ret = usb_submit_urb(urb, GFP_ATOMIC);
+	if (ret)
+		err("usb_submit_urb error (%d)", ret);
+}
+
+static void qcm_isoc_irq(struct urb *urb, struct pt_regs *regs)
+{
+	int len;
+	struct uvd *uvd = urb->context;
+
+	if (!CAMERA_IS_OPERATIONAL(uvd))
+		return;
+
+	if (!uvd->streaming) 
+		return;
+
+	uvd->stats.urb_count++;
+
+	if (!urb->actual_length) {
+		resubmit_urb(uvd, urb);
+		return;
+	}
+
+	len = qcm_compress_iso(uvd, urb);
+	resubmit_urb(uvd, urb);
+	uvd->stats.urb_length = len;
+	uvd->stats.data_count += len;
+	if (len)
+		RingQueue_WakeUpInterruptible(&uvd->dp);
+}
+
+static int qcm_start_data(struct uvd *uvd)
+{
+	struct qcm *cam = (struct qcm *) uvd->user_data;
+	int i;
+	int errflag;
+	int pktsz;
+	int err;
+
+	pktsz = uvd->iso_packet_len;
+	if (!CAMERA_IS_OPERATIONAL(uvd)) {
+		err("Camera is not operational");
+		return -EFAULT;
+	}
+
+	err = usb_set_interface(uvd->dev, uvd->iface, uvd->ifaceAltActive);
+	if (err < 0) {
+		err("usb_set_interface error");
+		uvd->last_error = err;
+		return -EBUSY;
+	}
+
+	for (i=0; i < USBVIDEO_NUMSBUF; i++) {
+		int j, k;
+		struct urb *urb = uvd->sbuf[i].urb;
+		urb->dev = uvd->dev;
+		urb->context = uvd;
+		urb->pipe = usb_rcvisocpipe(uvd->dev, uvd->video_endp);
+		urb->interval = 1;
+		urb->transfer_flags = URB_ISO_ASAP;
+		urb->transfer_buffer = uvd->sbuf[i].data;
+		urb->complete = qcm_isoc_irq;
+		urb->number_of_packets = FRAMES_PER_DESC;
+		urb->transfer_buffer_length = pktsz * FRAMES_PER_DESC;
+		for (j=k=0; j < FRAMES_PER_DESC; j++, k += pktsz) {
+			urb->iso_frame_desc[j].offset = k;
+			urb->iso_frame_desc[j].length = pktsz;
+		}
+	}
+
+	uvd->streaming = 1;
+	uvd->curframe = -1;
+	for (i=0; i < USBVIDEO_NUMSBUF; i++) {
+		errflag = usb_submit_urb(uvd->sbuf[i].urb, GFP_KERNEL);
+		if (errflag)
+			err ("usb_submit_isoc(%d) ret %d", i, errflag);
+	}
+
+	CHECK_RET(err, qcm_setup_input_int(cam, uvd));
+	CHECK_RET(err, qcm_camera_on(uvd));
+	return 0;
+}
+
+static void qcm_stop_data(struct uvd *uvd)
+{
+	struct qcm *cam = (struct qcm *) uvd->user_data;
+	int i, j;
+	int ret;
+	
+	if ((uvd == NULL) || (!uvd->streaming) || (uvd->dev == NULL))
+		return;
+
+	ret = qcm_camera_off(uvd);
+	if (ret) 
+		warn("couldn't turn the cam off.");
+
+	uvd->streaming = 0;
+
+	/* Unschedule all of the iso td's */
+	for (i=0; i < USBVIDEO_NUMSBUF; i++) 
+		usb_kill_urb(uvd->sbuf[i].urb);
+
+	qcm_stop_int_data(cam);
+
+	if (!uvd->remove_pending) {
+		/* Set packet size to 0 */
+		j = usb_set_interface(uvd->dev, uvd->iface, 
+					uvd->ifaceAltInactive);
+		if (j < 0) {
+			err("usb_set_interface() error %d.", j);
+			uvd->last_error = j;
+		}
+	}
+}
+
+static void qcm_process_isoc(struct uvd *uvd, struct usbvideo_frame *frame)
+{	
+	struct qcm *cam = (struct qcm *) uvd->user_data;
+	int x;
+	struct rgb *rgbL0;
+	struct rgb *rgbL1;
+	struct bayL0 *bayL0;
+	struct bayL1 *bayL1;
+	int hor,ver,hordel,verdel;
+	assert(frame != NULL);
+
+	switch (cam->size) {
+	case SIZE_160X120:
+		hor = 162; ver = 124; hordel = 1; verdel = 2;
+		break;
+	case SIZE_320X240:
+	default:
+		hor = 324; ver = 248; hordel = 2; verdel = 4;
+		break;
+	}
+	
+	if (frame->scanstate == ScanState_Scanning) {
+		while (RingQueue_GetLength(&uvd->dp) >=
+			 4 + (hor*verdel + hordel)) {
+			if ((RING_QUEUE_PEEK(&uvd->dp, 0) == 0x00) &&
+			    (RING_QUEUE_PEEK(&uvd->dp, 1) == 0xff) &&
+			    (RING_QUEUE_PEEK(&uvd->dp, 2) == 0x00) &&
+			    (RING_QUEUE_PEEK(&uvd->dp, 3) == 0xff)) {
+				frame->curline = 0;
+				frame->scanstate = ScanState_Lines;
+				frame->frameState = FrameState_Grabbing;
+				RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 4);
+			/* 
+			* if we're starting, we need to discard the first 
+			* 4 lines of y bayer data
+			* and the first 2 gr elements of x bayer data
+			*/	
+				RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 
+							(hor*verdel + hordel));
+				break;
+			}
+			RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 1);
+		}
+	}
+
+	if (frame->scanstate == ScanState_Scanning)
+		return;
+	
+	/* now we can start processing bayer data so long as we have at least
+	* 2 lines worth of data. this is the simplest demosaicing method that
+	* I could think of. I use each 2x2 bayer element without interpolation 
+	* to generate 4 rgb pixels. 
+	*/
+	while ( frame->curline < cam->height && 
+		(RingQueue_GetLength(&uvd->dp) >= hor*2)) {
+		/* get 2 lines of bayer for demosaicing
+		 * into 2 lines of RGB */
+		RingQueue_Dequeue(&uvd->dp, cam->scratch, hor*2);
+		bayL0 = (struct bayL0 *) cam->scratch;
+		bayL1 = (struct bayL1 *) (cam->scratch + hor);
+		/* frame->curline is the rgb y line */
+		rgbL0 = (struct rgb *) 
+				( frame->data + (cam->width*3*frame->curline));
+		/* w/2 because we're already doing 2 pixels */
+		rgbL1 = rgbL0 + (cam->width/2);
+
+		for (x=0; x < cam->width; x+=2) {
+			rgbL0->r = bayL0->r;
+			rgbL0->g = bayL0->g;
+			rgbL0->b = bayL1->b;
+
+			rgbL0->r2 = bayL0->r;
+			rgbL0->g2 = bayL1->g;
+			rgbL0->b2 = bayL1->b;
+
+			rgbL1->r = bayL0->r;
+			rgbL1->g = bayL1->g;
+			rgbL1->b = bayL1->b;
+
+			rgbL1->r2 = bayL0->r;
+			rgbL1->g2 = bayL1->g;
+			rgbL1->b2 = bayL1->b;
+
+			rgbL0++;
+			rgbL1++;
+	
+			bayL0++;
+			bayL1++;
+		}
+
+		frame->seqRead_Length += cam->width*3*2;
+		frame->curline += 2;
+	}
+	/* See if we filled the frame */
+	if (frame->curline == cam->height) {
+		frame->frameState = FrameState_Done_Hold;
+		frame->curline = 0;
+		uvd->curframe = -1;
+		uvd->stats.frame_num++;
+	}
+}
+
+/* taken from konicawc */
+static int qcm_set_video_mode(struct uvd *uvd, struct video_window *vw)
+{
+	int ret;
+	int newsize;
+	int oldsize;
+	int x = vw->width;
+	int y = vw->height;
+	struct qcm *cam = (struct qcm *) uvd->user_data;
+
+	if (x > 0 && y > 0) {
+		DEBUG(2, "trying to find size %d,%d", x, y);
+		for (newsize = 0; newsize <= MAX_FRAME_SIZE; newsize++) {
+			if ((camera_sizes[newsize].width == x) && 
+				(camera_sizes[newsize].height == y))
+				break;
+		}
+	} else 
+		newsize = cam->size;
+
+	if (newsize > MAX_FRAME_SIZE) {
+		DEBUG(1, "couldn't find size %d,%d", x, y);
+		return -EINVAL;
+	}
+
+	if (newsize == cam->size) {
+		DEBUG(1, "Nothing to do");
+		return 0;
+	}
+
+	qcm_stop_data(uvd);
+
+	if (cam->size != newsize) {
+		oldsize = cam->size; 
+		cam->size = newsize;
+		ret = qcm_set_camera_size(uvd);
+		if (ret) {
+			err("Couldn't set camera size, err=%d",ret);
+			/* restore the original size */
+			cam->size = oldsize;
+			return ret;
+		}
+	}
+
+	/* Flush the input queue and clear any current frame in progress */
+
+	RingQueue_Flush(&uvd->dp);
+	if (uvd->curframe != -1) {
+		uvd->frame[uvd->curframe].curline = 0;
+		uvd->frame[uvd->curframe].seqRead_Length = 0;
+		uvd->frame[uvd->curframe].seqRead_Index = 0;
+	}
+
+	CHECK_RET(ret, qcm_start_data(uvd));
+	return 0;
+}
+
+static int qcm_configure_video(struct uvd *uvd)
+{
+	int ret;
+	memset(&uvd->vpic, 0, sizeof(uvd->vpic));
+	memset(&uvd->vpic_old, 0x55, sizeof(uvd->vpic_old));
+
+	uvd->vpic.colour = colour;
+	uvd->vpic.hue = hue;
+	uvd->vpic.brightness = brightness;
+	uvd->vpic.contrast = contrast;
+	uvd->vpic.whiteness = whiteness;
+	uvd->vpic.depth = 24;
+	uvd->vpic.palette = VIDEO_PALETTE_RGB24;
+
+	memset(&uvd->vcap, 0, sizeof(uvd->vcap));
+	strcpy(uvd->vcap.name, "QCM USB Camera");
+	uvd->vcap.type = VID_TYPE_CAPTURE;
+	uvd->vcap.channels = 1;
+	uvd->vcap.audios = 0;
+
+	uvd->vcap.minwidth = camera_sizes[SIZE_160X120].width;
+	uvd->vcap.minheight = camera_sizes[SIZE_160X120].height;
+	uvd->vcap.maxwidth = camera_sizes[SIZE_320X240].width;
+	uvd->vcap.maxheight = camera_sizes[SIZE_320X240].height;
+
+	memset(&uvd->vchan, 0, sizeof(uvd->vchan));
+	uvd->vchan.flags = 0 ;
+	uvd->vchan.tuners = 0;
+	uvd->vchan.channel = 0;
+	uvd->vchan.type = VIDEO_TYPE_CAMERA;
+	strcpy(uvd->vchan.name, "Camera");
+
+	CHECK_RET(ret, qcm_sensor_init(uvd));
+	return 0;
+}
+
+static int qcm_probe(struct usb_interface *intf, 
+			const struct usb_device_id *devid)
+{
+	int err;
+	struct uvd *uvd;
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct qcm *cam;
+	size_t buffer_size;
+	unsigned char video_ep;
+	struct usb_host_interface *interface;
+	struct usb_endpoint_descriptor *endpoint;
+	int i,j;
+	unsigned int ifacenum, ifacenum_inact=0;
+	__le16 sensor_id;
+
+	/* we don't support multiconfig cams */
+	if (dev->descriptor.bNumConfigurations != 1)
+		return -ENODEV;
+
+	/* first check for the video interface and not
+	* the audio interface */
+	interface = &intf->cur_altsetting[0];
+	if ((interface->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC) 
+		|| (interface->desc.bInterfaceSubClass != 
+			USB_CLASS_VENDOR_SPEC))
+		return -ENODEV;
+
+	/* 
+	walk through each endpoint in each setting in the interface
+	stop when we find the one that's an isochronous IN endpoint.
+	*/
+	for (i=0; i < intf->num_altsetting; i++) {
+		interface = &intf->cur_altsetting[i];
+		ifacenum = interface->desc.bAlternateSetting;
+		/* walk the end points */
+		for (j=0; j < interface->desc.bNumEndpoints; j++) {
+			endpoint = &interface->endpoint[j].desc;
+
+			if ((endpoint->bEndpointAddress & 
+				USB_ENDPOINT_DIR_MASK) != USB_DIR_IN)
+				continue; /* not input then not good */
+
+			buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+			if (!buffer_size) {
+				ifacenum_inact = ifacenum;
+				continue; /* 0 pkt size is not what we want */
+			}
+
+			if ((endpoint->bmAttributes & 
+				USB_ENDPOINT_XFERTYPE_MASK) == 
+				USB_ENDPOINT_XFER_ISOC) {
+				video_ep = endpoint->bEndpointAddress;
+				/* break out of the search */
+				goto good_videoep;
+			}
+		}
+	}
+	/* failed out since nothing useful was found */
+	err("No suitable endpoint was found\n");
+	return -ENODEV;
+
+good_videoep:	
+	/* disable isochronous stream before doing anything else */ 
+	err = qcm_stv_setb(dev, STV_ISO_ENABLE, 0); 
+	if (err < 0) {
+		err("Failed to disable sensor stream");
+		return -EIO;
+	}
+	
+	/*
+	Check that this is the same unknown sensor that is known to work. This
+	sensor is suspected to be the ST VV6422C001. I'll check the same value
+	that the qc-usb driver checks. This value is probably not even the
+	sensor ID since it matches the USB dev ID. Oh well. If it doesn't 
+	match, it's probably a diff sensor so exit and apologize. 
+	*/
+	err = qcm_stv_getw(dev, CMOS_SENSOR_IDREV, &sensor_id);
+	if (err < 0) {
+		err("Couldn't read sensor values. Err %d\n",err);
+		return err;
+	}
+	if (sensor_id != cpu_to_le16(0x08F0)) {
+		err("Sensor ID %x != %x. Unsupported. Sorry\n", 
+			le16_to_cpu(sensor_id), (0x08F0));
+		return -ENODEV;
+	}
+	
+	uvd = usbvideo_AllocateDevice(cams);
+	if (!uvd) 
+		return -ENOMEM;
+
+	cam = (struct qcm *) uvd->user_data;
+
+	/* buf for doing demosaicing */ 
+	cam->scratch = kmalloc(324*2, GFP_KERNEL); 
+	if (!cam->scratch) /* uvd freed in dereg */
+		return -ENOMEM;	
+
+	/* yes, if we fail after here, cam->scratch gets freed
+	by qcm_free_uvd */
+
+	err = qcm_alloc_int_urb(cam);
+	if (err < 0) 
+		return err;
+
+	/* yes, if we fail after here, int urb gets freed
+	by qcm_free_uvd */
+
+	RESTRICT_TO_RANGE(size, SIZE_160X120, SIZE_320X240);
+	cam->width = camera_sizes[size].width;
+	cam->height = camera_sizes[size].height;
+	cam->size = size;
+
+	uvd->debug = debug;
+	uvd->flags = 0;
+	uvd->dev = dev;
+	uvd->iface = intf->altsetting->desc.bInterfaceNumber;
+	uvd->ifaceAltActive = ifacenum;
+	uvd->ifaceAltInactive = ifacenum_inact;
+	uvd->video_endp = video_ep;
+	uvd->iso_packet_len = buffer_size;
+	uvd->paletteBits = 1L << VIDEO_PALETTE_RGB24;
+	uvd->defaultPalette = VIDEO_PALETTE_RGB24;
+	uvd->canvas = VIDEOSIZE(320, 240);
+	uvd->videosize = VIDEOSIZE(cam->width, cam->height);
+	err = qcm_configure_video(uvd);
+	if (err) {
+		err("failed to configure video settings");
+		return err;
+	}
+
+	err = usbvideo_RegisterVideoDevice(uvd);	
+	if (err) { /* the uvd gets freed in Deregister */
+		err("usbvideo_RegisterVideoDevice() failed.");
+		return err;
+	}	
+
+	uvd->max_frame_size = (320 * 240 * 3);
+	qcm_register_input(cam, dev);
+	usb_set_intfdata(intf, uvd);
+	return 0;
+}
+
+static void qcm_free_uvd(struct uvd *uvd)
+{
+	struct qcm *cam = (struct qcm *) uvd->user_data;
+
+	kfree(cam->scratch);
+	qcm_unregister_input(cam);
+	qcm_free_int(cam);
+}
+
+static struct usbvideo_cb qcm_driver = {
+	.probe = 		qcm_probe,
+	.setupOnOpen = 		qcm_setup_on_open,
+	.processData = 		qcm_process_isoc,
+	.setVideoMode = 	qcm_set_video_mode,
+	.startDataPump = 	qcm_start_data,
+	.stopDataPump = 	qcm_stop_data,
+	.adjustPicture = 	qcm_adjust_picture,
+	.userFree = 		qcm_free_uvd
+};
+
+static int __init qcm_init(void)
+{
+	info(DRIVER_DESC " " DRIVER_VERSION);
+
+	return usbvideo_register(
+		&cams,
+		MAX_CAMERAS,
+		sizeof(struct qcm),
+		"QCM",
+		&qcm_driver,
+		THIS_MODULE,
+		qcm_table);
+}
+
+static void __exit qcm_exit(void)
+{
+	usbvideo_Deregister(&cams);
+}
+
+module_param(size, int, 0);
+MODULE_PARM_DESC(size, "Initial Size 0: 160x120 1: 320x240");
+module_param(colour, int, 0);
+MODULE_PARM_DESC(colour, "Initial colour");
+module_param(hue, int, 0);
+MODULE_PARM_DESC(hue, "Initial hue");
+module_param(brightness, int, 0);
+MODULE_PARM_DESC(brightness, "Initial brightness");
+module_param(contrast, int, 0);
+MODULE_PARM_DESC(contrast, "Initial contrast");
+module_param(whiteness, int, 0);
+MODULE_PARM_DESC(whiteness, "Initial whiteness");
+
+#ifdef CONFIG_USB_DEBUG
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug level: 0-9 (default=0)");
+#endif
+
+module_init(qcm_init);
+module_exit(qcm_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jaya Kumar");
+MODULE_DESCRIPTION("QCM USB Camera");
+MODULE_SUPPORTED_DEVICE("QCM USB Camera");
diff -X linux-2.6.17-rc4/Documentation/dontdiff -uprN linux-2.6.17-rc4-vanilla/drivers/media/video/usbvideo/quickcam_messenger.h linux-2.6.17-rc4/drivers/media/video/usbvideo/quickcam_messenger.h
--- linux-2.6.17-rc4-vanilla/drivers/media/video/usbvideo/quickcam_messenger.h	1970-01-01 07:30:00.000000000 +0730
+++ linux-2.6.17-rc4/drivers/media/video/usbvideo/quickcam_messenger.h	2006-05-17 08:37:05.000000000 +0800
@@ -0,0 +1,126 @@
+#ifndef quickcam_messenger_h
+#define quickcam_messenger_h
+
+#ifndef CONFIG_INPUT
+/* if we're not using input we dummy out these functions */
+#define qcm_register_input(...)
+#define qcm_unregister_input(...)
+#define qcm_report_buttonstat(...)
+#define qcm_setup_input_int(...) 0
+#define qcm_stop_int_data(...)
+#define qcm_alloc_int_urb(...) 0
+#define qcm_free_int(...)
+#endif
+
+
+#define CHECK_RET(ret, expr) \
+	if ((ret = expr) < 0) return ret
+
+/* Control Registers for the STVV6422 ASIC
+ * - this define is taken from the qc-usb-messenger code
+ */
+#define STV_ISO_ENABLE		0x1440
+#define ISOC_PACKET_SIZE	1023	
+
+/* Chip identification number including revision indicator */
+#define CMOS_SENSOR_IDREV	0xE00A
+
+struct rgb {
+	u8 b;
+	u8 g;
+	u8 r;
+	u8 b2;
+	u8 g2;
+	u8 r2;
+};
+
+struct bayL0 {
+#ifdef __BIG_ENDIAN
+	u8 r;
+	u8 g;
+#elif __LITTLE_ENDIAN
+	u8 g;
+	u8 r;
+#else
+#error not byte order defined
+#endif
+};
+
+struct bayL1 {
+#ifdef __BIG_ENDIAN
+	u8 g;
+	u8 b;
+#elif __LITTLE_ENDIAN
+	u8 b;
+	u8 g;
+#else
+#error not byte order defined
+#endif
+};
+
+struct cam_size {
+	u16	width;
+	u16	height;
+	u8	cmd;
+};
+
+static const struct cam_size camera_sizes[] = { 
+	{ 160, 120, 0xf }, 
+	{ 320, 240, 0x2 }, 
+};
+
+enum frame_sizes {
+	SIZE_160X120	= 0,
+	SIZE_320X240	= 1,
+};
+
+#define MAX_FRAME_SIZE SIZE_320X240
+
+struct qcm {
+	u16 colour;
+	u16 hue;
+	u16 brightness;
+	u16 contrast;
+	u16 whiteness;
+
+	u8 size;
+	int height;
+	int width;
+	u8 *scratch;
+	struct urb *button_urb;
+	u8 button_sts;
+	u8 button_sts_buf;
+
+#ifdef CONFIG_INPUT
+	struct input_dev *input;
+	char input_physname[64];
+#endif
+};
+
+struct regval {
+	u16 reg;
+	u8 val;
+};
+/* this table is derived from the 
+qc-usb-messenger code */
+static const struct regval regval_table[] = {
+	{ STV_ISO_ENABLE, 0x00 },	
+	{ 0x1436, 0x00 }, { 0x1432, 0x03 },	
+	{ 0x143a, 0xF9 }, { 0x0509, 0x38 },
+	{ 0x050a, 0x38 }, { 0x050b, 0x38 },
+	{ 0x050c, 0x2A }, { 0x050d, 0x01 },
+	{ 0x1431, 0x00 }, { 0x1433, 0x34 },
+	{ 0x1438, 0x18 }, { 0x1439, 0x00 },
+	{ 0x143b, 0x05 }, { 0x143c, 0x00 },
+	{ 0x143e, 0x01 }, { 0x143d, 0x00 },
+	{ 0x1442, 0xe2 }, { 0x1500, 0xd0 },
+	{ 0x1500, 0xd0 }, { 0x1500, 0x50 },
+	{ 0x1501, 0xaf }, { 0x1502, 0xc2 },
+	{ 0x1503, 0x45 }, { 0x1505, 0x02 },
+	{ 0x150e, 0x8e }, { 0x150f, 0x37 },
+	{ 0x15c0, 0x00 },
+};
+
+static const unsigned char marker[] = { 0x00, 0xff, 0x00, 0xFF };
+
+#endif /* quickcam_messenger_h */

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [linux-usb-devel] [PATCH/RFC 2.6.17-rc4 1/1] usbvideo/quickcam_messenger driver v4
  2006-05-17  2:36 [PATCH/RFC 2.6.17-rc4 1/1] usbvideo/quickcam_messenger driver v4 jayakumar.video
@ 2006-05-17  6:14 ` Jaya Kumar
  2006-05-17 16:35   ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 5+ messages in thread
From: Jaya Kumar @ 2006-05-17  6:14 UTC (permalink / raw)
  To: linux-kernel, linux-usb-devel; +Cc: mchehab, video4linux-list

[ My apologies again. Adding Mauro and video4linux-list to CC list. ]

On 5/17/06, jayakumar.video@gmail.com <jayakumar.video@gmail.com> wrote:
> [ sorry, bungled the l-u-devel list address and subject in prev email ]
>
> Hi Greg KH, USB folk, kernel folk,
>
> Appended is v4 of my patch adding a usbvideo driver for the Logitech
> Quickcam Messenger USB webcam. This patch has been rebased against
> 2.6.17-rc4 as per Greg's feedback.
>
> Please let me know if it looks okay and if you have any feedback
> or suggestions.
>
> Thanks,
> Jaya Kumar
>
> Signed-off-by: Jaya Kumar <jayakumar.video@gmail.com>
>
> ---
>
>  Kconfig              |   12
>  Makefile             |    1
>  quickcam_messenger.c | 1120 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  quickcam_messenger.h |  126 +++++
>  4 files changed, 1259 insertions(+)
>
> ---
>
> diff -X linux-2.6.17-rc4/Documentation/dontdiff -uprN linux-2.6.17-rc4-vanilla/drivers/media/video/usbvideo/Kconfig linux-2.6.17-rc4/drivers/media/video/usbvideo/Kconfig
> --- linux-2.6.17-rc4-vanilla/drivers/media/video/usbvideo/Kconfig       2006-05-17 08:11:36.000000000 +0800
> +++ linux-2.6.17-rc4/drivers/media/video/usbvideo/Kconfig       2006-05-17 08:39:51.674492008 +0800
> @@ -36,3 +36,15 @@ config USB_KONICAWC
>
>          To compile this driver as a module, choose M here: the
>          module will be called konicawc.
> +
> +config USB_QUICKCAM_MESSENGER
> +       tristate "USB Logitech Quickcam Messenger"
> +       depends on USB && VIDEO_DEV
> +       select VIDEO_USBVIDEO
> +       ---help---
> +         Say Y or M here to enable support for the USB Logitech Quickcam
> +          Messenger webcam.
> +
> +         To compile this driver as a module, choose M here: the
> +         module will be called quickcam_messenger.
> +
> diff -X linux-2.6.17-rc4/Documentation/dontdiff -uprN linux-2.6.17-rc4-vanilla/drivers/media/video/usbvideo/Makefile linux-2.6.17-rc4/drivers/media/video/usbvideo/Makefile
> --- linux-2.6.17-rc4-vanilla/drivers/media/video/usbvideo/Makefile      2006-05-17 08:11:36.000000000 +0800
> +++ linux-2.6.17-rc4/drivers/media/video/usbvideo/Makefile      2006-05-17 08:40:14.000000000 +0800
> @@ -2,3 +2,4 @@ obj-$(CONFIG_VIDEO_USBVIDEO)    += usbvi
>  obj-$(CONFIG_USB_IBMCAM)        += ibmcam.o ultracam.o
>  obj-$(CONFIG_USB_KONICAWC)      += konicawc.o
>  obj-$(CONFIG_USB_VICAM)         += vicam.o
> +obj-$(CONFIG_USB_QUICKCAM_MESSENGER)   += quickcam_messenger.o
> diff -X linux-2.6.17-rc4/Documentation/dontdiff -uprN linux-2.6.17-rc4-vanilla/drivers/media/video/usbvideo/quickcam_messenger.c linux-2.6.17-rc4/drivers/media/video/usbvideo/quickcam_messenger.c
> --- linux-2.6.17-rc4-vanilla/drivers/media/video/usbvideo/quickcam_messenger.c  1970-01-01 07:30:00.000000000 +0730
> +++ linux-2.6.17-rc4/drivers/media/video/usbvideo/quickcam_messenger.c  2006-05-17 08:37:05.000000000 +0800
> @@ -0,0 +1,1120 @@
> +/*
> + * Driver for Logitech Quickcam Messenger usb video camera
> + * Copyright (C) Jaya Kumar
> + *
> + * This work was sponsored by CIS(M) Sdn Bhd.
> + * History:
> + * 05/08/2006 - Jaya Kumar
> + * I wrote this based on the konicawc by Simon Evans.
> + * -
> + * Full credit for reverse engineering and creating an initial
> + * working linux driver for the VV6422 goes to the qce-ga project by
> + * Tuukka Toivonen, Jochen Hoenicke, Peter McConnell,
> + * Cristiano De Michele, Georg Acher, Jean-Frederic Clere as well as
> + * others.
> + * ---
> + * 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
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/input.h>
> +#include <linux/usb_input.h>
> +
> +#include "usbvideo.h"
> +#include "quickcam_messenger.h"
> +
> +/*
> + * Version Information
> + */
> +
> +#ifdef CONFIG_USB_DEBUG
> +static int debug;
> +#define DEBUG(n, format, arg...) \
> +       if (n <= debug) {        \
> +               printk(KERN_DEBUG __FILE__ ":%s(): " format "\n", __FUNCTION__ , ## arg); \
> +       }
> +#else
> +#define DEBUG(n, arg...)
> +static const int debug = 0;
> +#endif
> +
> +#define DRIVER_VERSION "v0.01"
> +#define DRIVER_DESC "Logitech Quickcam Messenger USB"
> +
> +#define USB_LOGITECH_VENDOR_ID 0x046D
> +#define USB_QCM_PRODUCT_ID     0x08F0
> +
> +#define MAX_CAMERAS    1
> +
> +#define MAX_COLOUR     32768
> +#define MAX_HUE                32768
> +#define MAX_BRIGHTNESS 32768
> +#define MAX_CONTRAST   32768
> +#define MAX_WHITENESS  32768
> +
> +static int size = SIZE_320X240;
> +static int colour = MAX_COLOUR;
> +static int hue = MAX_HUE;
> +static int brightness =        MAX_BRIGHTNESS;
> +static int contrast =  MAX_CONTRAST;
> +static int whiteness = MAX_WHITENESS;
> +
> +static struct usbvideo *cams;
> +
> +static struct usb_device_id qcm_table [] = {
> +       { USB_DEVICE(USB_LOGITECH_VENDOR_ID, USB_QCM_PRODUCT_ID) },
> +       { }
> +};
> +MODULE_DEVICE_TABLE(usb, qcm_table);
> +
> +#ifdef CONFIG_INPUT
> +static void qcm_register_input(struct qcm *cam, struct usb_device *dev)
> +{
> +       struct input_dev *input_dev;
> +
> +       usb_make_path(dev, cam->input_physname, sizeof(cam->input_physname));
> +       strncat(cam->input_physname, "/input0", sizeof(cam->input_physname));
> +
> +       cam->input = input_dev = input_allocate_device();
> +       if (!input_dev) {
> +               warn("insufficient mem for cam input device");
> +               return;
> +       }
> +
> +       input_dev->name = "QCM button";
> +       input_dev->phys = cam->input_physname;
> +       usb_to_input_id(dev, &input_dev->id);
> +       input_dev->cdev.dev = &dev->dev;
> +
> +       input_dev->evbit[0] = BIT(EV_KEY);
> +       input_dev->keybit[LONG(BTN_0)] = BIT(BTN_0);
> +
> +       input_dev->private = cam;
> +
> +       input_register_device(cam->input);
> +}
> +
> +static void qcm_unregister_input(struct qcm *cam)
> +{
> +       if (cam->input) {
> +               input_unregister_device(cam->input);
> +               cam->input = NULL;
> +       }
> +}
> +
> +static void qcm_report_buttonstat(struct qcm *cam)
> +{
> +       if (cam->input) {
> +               input_report_key(cam->input, BTN_0, cam->button_sts);
> +               input_sync(cam->input);
> +       }
> +}
> +
> +static void qcm_int_irq(struct urb *urb, struct pt_regs *regs)
> +{
> +       int ret;
> +       struct uvd *uvd = urb->context;
> +       struct qcm *cam;
> +
> +       if (!CAMERA_IS_OPERATIONAL(uvd))
> +               return;
> +
> +       if (!uvd->streaming)
> +               return;
> +
> +       uvd->stats.urb_count++;
> +
> +       if (urb->status < 0)
> +               uvd->stats.iso_err_count++;
> +       else {
> +               if (urb->actual_length > 0 ) {
> +                       cam = (struct qcm *) uvd->user_data;
> +                       if (cam->button_sts_buf == 0x88)
> +                               cam->button_sts = 0x0;
> +                       else if (cam->button_sts_buf == 0x80)
> +                               cam->button_sts = 0x1;
> +                       qcm_report_buttonstat(cam);
> +               }
> +       }
> +
> +       ret = usb_submit_urb(urb, GFP_ATOMIC);
> +       if (ret < 0)
> +               err("usb_submit_urb error (%d)", ret);
> +}
> +
> +static int qcm_setup_input_int(struct qcm *cam, struct uvd *uvd)
> +{
> +       int errflag;
> +       usb_fill_int_urb(cam->button_urb, uvd->dev,
> +                       usb_rcvintpipe(uvd->dev, uvd->video_endp + 1),
> +                       &cam->button_sts_buf,
> +                       1,
> +                       qcm_int_irq,
> +                       uvd, 16);
> +
> +       errflag = usb_submit_urb(cam->button_urb, GFP_KERNEL);
> +       if (errflag)
> +               err ("usb_submit_int ret %d", errflag);
> +       return errflag;
> +}
> +
> +static void qcm_stop_int_data(struct qcm *cam)
> +{
> +       usb_kill_urb(cam->button_urb);
> +}
> +
> +static int qcm_alloc_int_urb(struct qcm *cam)
> +{
> +       cam->button_urb = usb_alloc_urb(0, GFP_KERNEL);
> +
> +       if (!cam->button_urb)
> +               return -ENOMEM;
> +
> +       return 0;
> +}
> +
> +static void qcm_free_int(struct qcm *cam)
> +{
> +       if (cam->button_urb)
> +               usb_free_urb(cam->button_urb);
> +}
> +#endif /* CONFIG_INPUT */
> +
> +static int qcm_stv_setb(struct usb_device *dev, u16 reg, u8 val)
> +{
> +       int ret;
> +
> +       /* we'll wait up to 3 slices but no more */
> +       ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
> +               0x04, USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
> +               reg, 0, &val, 1, 3*HZ);
> +       return ret;
> +}
> +
> +static int qcm_stv_setw(struct usb_device *dev, u16 reg, u16 val)
> +{
> +       int ret;
> +
> +       /* we'll wait up to 3 slices but no more */
> +       ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
> +               0x04, USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
> +               reg, 0, &val, 2, 3*HZ);
> +       return ret;
> +}
> +
> +static int qcm_stv_getw(struct usb_device *dev, unsigned short reg,
> +                                                       __le16 *val)
> +{
> +       int ret;
> +
> +       /* we'll wait up to 3 slices but no more */
> +       ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
> +               0x04, USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE,
> +               reg, 0, val, 2, 3*HZ);
> +       return ret;
> +}
> +
> +static int qcm_camera_on(struct uvd *uvd)
> +{
> +       int ret;
> +       CHECK_RET(ret, qcm_stv_setb(uvd->dev, STV_ISO_ENABLE, 0x01));
> +       return 0;
> +}
> +
> +static int qcm_camera_off(struct uvd *uvd)
> +{
> +       int ret;
> +       CHECK_RET(ret, qcm_stv_setb(uvd->dev, STV_ISO_ENABLE, 0x00));
> +       return 0;
> +}
> +
> +static void qcm_hsv2rgb(u16 hue, u16 sat, u16 val, u16 *r, u16 *g, u16 *b)
> +{
> +       unsigned int segment, valsat;
> +       signed int   h = (signed int) hue;
> +       unsigned int s = (sat - 32768) * 2;     /* rescale */
> +       unsigned int v = val;
> +       unsigned int p;
> +
> +       /*
> +       the registers controling gain are 8 bit of which
> +       we affect only the last 4 bits with our gain.
> +       we know that if saturation is 0, (unsaturated) then
> +       we're grayscale (center axis of the colour cone) so
> +       we set rgb=value. we use a formula obtained from
> +       wikipedia to map the cone to the RGB plane. it's
> +       as follows for the human value case of h=0..360,
> +       s=0..1, v=0..1
> +       h_i = h/60 % 6 , f = h/60 - h_i , p = v(1-s)
> +       q = v(1 - f*s) , t = v(1 - (1-f)s)
> +       h_i==0 => r=v , g=t, b=p
> +       h_i==1 => r=q , g=v, b=p
> +       h_i==2 => r=p , g=v, b=t
> +       h_i==3 => r=p , g=q, b=v
> +       h_i==4 => r=t , g=p, b=v
> +       h_i==5 => r=v , g=p, b=q
> +       the bottom side (the point) and the stuff just up
> +       of that is black so we simplify those two cases.
> +       */
> +       if (sat < 32768) {
> +               /* anything less than this is unsaturated */
> +               *r = val;
> +               *g = val;
> +               *b = val;
> +               return;
> +       }
> +       if (val <= (0xFFFF/8)) {
> +               /* anything less than this is black */
> +               *r = 0;
> +               *g = 0;
> +               *b = 0;
> +               return;
> +       }
> +
> +       /* the rest of this code is copying tukkat's
> +       implementation of the hsv2rgb conversion as taken
> +       from qc-usb-messenger code. the 10923 is 0xFFFF/6
> +       to divide the cone into 6 sectors.  */
> +
> +       segment = (h + 10923) & 0xFFFF;
> +       segment = segment*3 >> 16;              /* 0..2: 0=R, 1=G, 2=B */
> +       hue -= segment * 21845;                 /* -10923..10923 */
> +       h = hue;
> +       h *= 3;
> +       valsat = v*s >> 16;                     /* 0..65534 */
> +       p = v - valsat;
> +       if (h >= 0) {
> +               unsigned int t = v - (valsat * (32769 - h) >> 15);
> +               switch (segment) {
> +               case 0: /* R-> */
> +                       *r = v;
> +                       *g = t;
> +                       *b = p;
> +                       break;
> +               case 1: /* G-> */
> +                       *r = p;
> +                       *g = v;
> +                       *b = t;
> +                       break;
> +               case 2: /* B-> */
> +                       *r = t;
> +                       *g = p;
> +                       *b = v;
> +                       break;
> +               }
> +       } else {
> +               unsigned int q = v - (valsat * (32769 + h) >> 15);
> +               switch (segment) {
> +               case 0: /* ->R */
> +                       *r = v;
> +                       *g = p;
> +                       *b = q;
> +                       break;
> +               case 1: /* ->G */
> +                       *r = q;
> +                       *g = v;
> +                       *b = p;
> +                       break;
> +               case 2: /* ->B */
> +                       *r = p;
> +                       *g = q;
> +                       *b = v;
> +                       break;
> +               }
> +       }
> +}
> +
> +static int qcm_sensor_set_gains(struct uvd *uvd, u16 hue,
> +       u16 saturation, u16 value)
> +{
> +       int ret;
> +       u16 r,g,b;
> +
> +       /* this code is based on qc-usb-messenger */
> +       qcm_hsv2rgb(hue, saturation, value, &r, &g, &b);
> +
> +       r >>= 12;
> +       g >>= 12;
> +       b >>= 12;
> +
> +       /* min val is 8 */
> +       r = max((u16) 8, r);
> +       g = max((u16) 8, g);
> +       b = max((u16) 8, b);
> +
> +       r |= 0x30;
> +       g |= 0x30;
> +       b |= 0x30;
> +
> +       /* set the r,g,b gain registers */
> +       CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x0509, r));
> +       CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x050A, g));
> +       CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x050B, b));
> +
> +       /* doing as qc-usb did */
> +       CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x050C, 0x2A));
> +       CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x050D, 0x01));
> +       CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143F, 0x01));
> +
> +       return 0;
> +}
> +
> +static int qcm_sensor_set_exposure(struct uvd *uvd, int exposure)
> +{
> +       int ret;
> +       int formedval;
> +
> +       /* calculation was from qc-usb-messenger driver */
> +       formedval = ( exposure >> 12 );
> +
> +       /* max value for formedval is 14 */
> +       formedval = min(formedval, 14);
> +
> +       CHECK_RET(ret, qcm_stv_setb(uvd->dev,
> +                       0x143A, 0xF0 | formedval));
> +       CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143F, 0x01));
> +       return 0;
> +}
> +
> +static int qcm_sensor_setlevels(struct uvd *uvd, int brightness, int contrast,
> +                                       int hue, int colour)
> +{
> +       int ret;
> +       /* brightness is exposure, contrast is gain, colour is saturation */
> +       CHECK_RET(ret,
> +               qcm_sensor_set_exposure(uvd, brightness));
> +       CHECK_RET(ret, qcm_sensor_set_gains(uvd, hue, colour, contrast));
> +
> +       return 0;
> +}
> +
> +static int qcm_sensor_setsize(struct uvd *uvd, u8 size)
> +{
> +       int ret;
> +
> +       CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x1505, size));
> +       return 0;
> +}
> +
> +static int qcm_sensor_set_shutter(struct uvd *uvd, int whiteness)
> +{
> +       int ret;
> +       /* some rescaling as done by the qc-usb-messenger code */
> +       if (whiteness > 0xC000)
> +               whiteness = 0xC000 + (whiteness & 0x3FFF)*8;
> +
> +       CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143D,
> +                               (whiteness >> 8) & 0xFF));
> +       CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143E,
> +                               (whiteness >> 16) & 0x03));
> +       CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143F, 0x01));
> +
> +       return 0;
> +}
> +
> +static int qcm_sensor_init(struct uvd *uvd)
> +{
> +       struct qcm *cam = (struct qcm *) uvd->user_data;
> +       int ret;
> +       int i;
> +
> +       for (i=0; i < sizeof(regval_table)/sizeof(regval_table[0]) ; i++) {
> +               CHECK_RET(ret, qcm_stv_setb(uvd->dev,
> +                                       regval_table[i].reg,
> +                                       regval_table[i].val));
> +       }
> +
> +       CHECK_RET(ret, qcm_stv_setw(uvd->dev, 0x15c1,
> +                               cpu_to_le16(ISOC_PACKET_SIZE)));
> +       CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x15c3, 0x08));
> +       CHECK_RET(ret, ret = qcm_stv_setb(uvd->dev, 0x143f, 0x01));
> +
> +       CHECK_RET(ret, qcm_stv_setb(uvd->dev, STV_ISO_ENABLE, 0x00));
> +
> +       CHECK_RET(ret, qcm_sensor_setsize(uvd, camera_sizes[cam->size].cmd));
> +
> +       CHECK_RET(ret, qcm_sensor_setlevels(uvd, uvd->vpic.brightness,
> +                       uvd->vpic.contrast, uvd->vpic.hue, uvd->vpic.colour));
> +
> +       CHECK_RET(ret, qcm_sensor_set_shutter(uvd, uvd->vpic.whiteness));
> +       CHECK_RET(ret, qcm_sensor_setsize(uvd, camera_sizes[cam->size].cmd));
> +
> +       return 0;
> +}
> +
> +static int qcm_set_camera_size(struct uvd *uvd)
> +{
> +       int ret;
> +       struct qcm *cam = (struct qcm *) uvd->user_data;
> +
> +       CHECK_RET(ret, qcm_sensor_setsize(uvd, camera_sizes[cam->size].cmd));
> +       cam->width = camera_sizes[cam->size].width;
> +       cam->height = camera_sizes[cam->size].height;
> +       uvd->videosize = VIDEOSIZE(cam->width, cam->height);
> +
> +       return 0;
> +}
> +
> +static int qcm_setup_on_open(struct uvd *uvd)
> +{
> +       int ret;
> +
> +       CHECK_RET(ret, qcm_sensor_set_gains(uvd, uvd->vpic.hue,
> +                               uvd->vpic.colour, uvd->vpic.contrast));
> +       CHECK_RET(ret, qcm_sensor_set_exposure(uvd, uvd->vpic.brightness));
> +       CHECK_RET(ret, qcm_sensor_set_shutter(uvd, uvd->vpic.whiteness));
> +       CHECK_RET(ret, qcm_set_camera_size(uvd));
> +       CHECK_RET(ret, qcm_camera_on(uvd));
> +       return 0;
> +}
> +
> +static void qcm_adjust_picture(struct uvd *uvd)
> +{
> +       int ret;
> +       struct qcm *cam = (struct qcm *) uvd->user_data;
> +
> +       ret = qcm_camera_off(uvd);
> +       if (ret) {
> +               err("can't turn camera off. abandoning pic adjustment");
> +               return;
> +       }
> +
> +       /* if there's been a change in contrast, hue, or
> +       colour then we need to recalculate hsv in order
> +       to update gains */
> +       if ((cam->contrast != uvd->vpic.contrast) ||
> +               (cam->hue != uvd->vpic.hue) ||
> +               (cam->colour != uvd->vpic.colour)) {
> +               cam->contrast = uvd->vpic.contrast;
> +               cam->hue = uvd->vpic.hue;
> +               cam->colour = uvd->vpic.colour;
> +               ret = qcm_sensor_set_gains(uvd, cam->hue, cam->colour,
> +                                               cam->contrast);
> +               if (ret) {
> +                       err("can't set gains. abandoning pic adjustment");
> +                       return;
> +               }
> +       }
> +
> +       if (cam->brightness != uvd->vpic.brightness) {
> +               cam->brightness = uvd->vpic.brightness;
> +               ret = qcm_sensor_set_exposure(uvd, cam->brightness);
> +               if (ret) {
> +                       err("can't set exposure. abandoning pic adjustment");
> +                       return;
> +               }
> +       }
> +
> +       if (cam->whiteness != uvd->vpic.whiteness) {
> +               cam->whiteness = uvd->vpic.whiteness;
> +               qcm_sensor_set_shutter(uvd, cam->whiteness);
> +               if (ret) {
> +                       err("can't set shutter. abandoning pic adjustment");
> +                       return;
> +               }
> +       }
> +
> +       ret = qcm_camera_on(uvd);
> +       if (ret) {
> +               err("can't reenable camera. pic adjustment failed");
> +               return;
> +       }
> +}
> +
> +static int qcm_process_frame(struct uvd *uvd, u8 *cdata, int framelen)
> +{
> +       int datalen;
> +       int totaldata;
> +       struct framehdr {
> +               __be16 id;
> +               __be16 len;
> +       };
> +       struct framehdr *fhdr;
> +
> +       totaldata = 0;
> +       while (framelen) {
> +               fhdr = (struct framehdr *) cdata;
> +               datalen = be16_to_cpu(fhdr->len);
> +               framelen -= 4;
> +               cdata += 4;
> +
> +               if ((fhdr->id) == cpu_to_be16(0x8001)) {
> +                       RingQueue_Enqueue(&uvd->dp, marker, 4);
> +                       totaldata += 4;
> +                       continue;
> +               }
> +               if ((fhdr->id & cpu_to_be16(0xFF00)) == cpu_to_be16(0x0200)) {
> +                       RingQueue_Enqueue(&uvd->dp, cdata, datalen);
> +                       totaldata += datalen;
> +               }
> +               framelen -= datalen;
> +               cdata += datalen;
> +       }
> +       return totaldata;
> +}
> +
> +static int qcm_compress_iso(struct uvd *uvd, struct urb *dataurb)
> +{
> +       int totlen;
> +       int i;
> +       unsigned char *cdata;
> +
> +       totlen=0;
> +       for (i = 0; i < dataurb->number_of_packets; i++) {
> +               int n = dataurb->iso_frame_desc[i].actual_length;
> +               int st = dataurb->iso_frame_desc[i].status;
> +
> +               cdata = dataurb->transfer_buffer +
> +                       dataurb->iso_frame_desc[i].offset;
> +
> +               if (st < 0) {
> +                       warn("Data error: packet=%d. len=%d. status=%d.",
> +                             i, n, st);
> +                       uvd->stats.iso_err_count++;
> +                       continue;
> +               }
> +               if (!n)
> +                       continue;
> +
> +               totlen += qcm_process_frame(uvd, cdata, n);
> +       }
> +       return totlen;
> +}
> +
> +static void resubmit_urb(struct uvd *uvd, struct urb *urb)
> +{
> +       int ret;
> +
> +       urb->dev = uvd->dev;
> +       ret = usb_submit_urb(urb, GFP_ATOMIC);
> +       if (ret)
> +               err("usb_submit_urb error (%d)", ret);
> +}
> +
> +static void qcm_isoc_irq(struct urb *urb, struct pt_regs *regs)
> +{
> +       int len;
> +       struct uvd *uvd = urb->context;
> +
> +       if (!CAMERA_IS_OPERATIONAL(uvd))
> +               return;
> +
> +       if (!uvd->streaming)
> +               return;
> +
> +       uvd->stats.urb_count++;
> +
> +       if (!urb->actual_length) {
> +               resubmit_urb(uvd, urb);
> +               return;
> +       }
> +
> +       len = qcm_compress_iso(uvd, urb);
> +       resubmit_urb(uvd, urb);
> +       uvd->stats.urb_length = len;
> +       uvd->stats.data_count += len;
> +       if (len)
> +               RingQueue_WakeUpInterruptible(&uvd->dp);
> +}
> +
> +static int qcm_start_data(struct uvd *uvd)
> +{
> +       struct qcm *cam = (struct qcm *) uvd->user_data;
> +       int i;
> +       int errflag;
> +       int pktsz;
> +       int err;
> +
> +       pktsz = uvd->iso_packet_len;
> +       if (!CAMERA_IS_OPERATIONAL(uvd)) {
> +               err("Camera is not operational");
> +               return -EFAULT;
> +       }
> +
> +       err = usb_set_interface(uvd->dev, uvd->iface, uvd->ifaceAltActive);
> +       if (err < 0) {
> +               err("usb_set_interface error");
> +               uvd->last_error = err;
> +               return -EBUSY;
> +       }
> +
> +       for (i=0; i < USBVIDEO_NUMSBUF; i++) {
> +               int j, k;
> +               struct urb *urb = uvd->sbuf[i].urb;
> +               urb->dev = uvd->dev;
> +               urb->context = uvd;
> +               urb->pipe = usb_rcvisocpipe(uvd->dev, uvd->video_endp);
> +               urb->interval = 1;
> +               urb->transfer_flags = URB_ISO_ASAP;
> +               urb->transfer_buffer = uvd->sbuf[i].data;
> +               urb->complete = qcm_isoc_irq;
> +               urb->number_of_packets = FRAMES_PER_DESC;
> +               urb->transfer_buffer_length = pktsz * FRAMES_PER_DESC;
> +               for (j=k=0; j < FRAMES_PER_DESC; j++, k += pktsz) {
> +                       urb->iso_frame_desc[j].offset = k;
> +                       urb->iso_frame_desc[j].length = pktsz;
> +               }
> +       }
> +
> +       uvd->streaming = 1;
> +       uvd->curframe = -1;
> +       for (i=0; i < USBVIDEO_NUMSBUF; i++) {
> +               errflag = usb_submit_urb(uvd->sbuf[i].urb, GFP_KERNEL);
> +               if (errflag)
> +                       err ("usb_submit_isoc(%d) ret %d", i, errflag);
> +       }
> +
> +       CHECK_RET(err, qcm_setup_input_int(cam, uvd));
> +       CHECK_RET(err, qcm_camera_on(uvd));
> +       return 0;
> +}
> +
> +static void qcm_stop_data(struct uvd *uvd)
> +{
> +       struct qcm *cam = (struct qcm *) uvd->user_data;
> +       int i, j;
> +       int ret;
> +
> +       if ((uvd == NULL) || (!uvd->streaming) || (uvd->dev == NULL))
> +               return;
> +
> +       ret = qcm_camera_off(uvd);
> +       if (ret)
> +               warn("couldn't turn the cam off.");
> +
> +       uvd->streaming = 0;
> +
> +       /* Unschedule all of the iso td's */
> +       for (i=0; i < USBVIDEO_NUMSBUF; i++)
> +               usb_kill_urb(uvd->sbuf[i].urb);
> +
> +       qcm_stop_int_data(cam);
> +
> +       if (!uvd->remove_pending) {
> +               /* Set packet size to 0 */
> +               j = usb_set_interface(uvd->dev, uvd->iface,
> +                                       uvd->ifaceAltInactive);
> +               if (j < 0) {
> +                       err("usb_set_interface() error %d.", j);
> +                       uvd->last_error = j;
> +               }
> +       }
> +}
> +
> +static void qcm_process_isoc(struct uvd *uvd, struct usbvideo_frame *frame)
> +{
> +       struct qcm *cam = (struct qcm *) uvd->user_data;
> +       int x;
> +       struct rgb *rgbL0;
> +       struct rgb *rgbL1;
> +       struct bayL0 *bayL0;
> +       struct bayL1 *bayL1;
> +       int hor,ver,hordel,verdel;
> +       assert(frame != NULL);
> +
> +       switch (cam->size) {
> +       case SIZE_160X120:
> +               hor = 162; ver = 124; hordel = 1; verdel = 2;
> +               break;
> +       case SIZE_320X240:
> +       default:
> +               hor = 324; ver = 248; hordel = 2; verdel = 4;
> +               break;
> +       }
> +
> +       if (frame->scanstate == ScanState_Scanning) {
> +               while (RingQueue_GetLength(&uvd->dp) >=
> +                        4 + (hor*verdel + hordel)) {
> +                       if ((RING_QUEUE_PEEK(&uvd->dp, 0) == 0x00) &&
> +                           (RING_QUEUE_PEEK(&uvd->dp, 1) == 0xff) &&
> +                           (RING_QUEUE_PEEK(&uvd->dp, 2) == 0x00) &&
> +                           (RING_QUEUE_PEEK(&uvd->dp, 3) == 0xff)) {
> +                               frame->curline = 0;
> +                               frame->scanstate = ScanState_Lines;
> +                               frame->frameState = FrameState_Grabbing;
> +                               RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 4);
> +                       /*
> +                       * if we're starting, we need to discard the first
> +                       * 4 lines of y bayer data
> +                       * and the first 2 gr elements of x bayer data
> +                       */
> +                               RING_QUEUE_DEQUEUE_BYTES(&uvd->dp,
> +                                                       (hor*verdel + hordel));
> +                               break;
> +                       }
> +                       RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 1);
> +               }
> +       }
> +
> +       if (frame->scanstate == ScanState_Scanning)
> +               return;
> +
> +       /* now we can start processing bayer data so long as we have at least
> +       * 2 lines worth of data. this is the simplest demosaicing method that
> +       * I could think of. I use each 2x2 bayer element without interpolation
> +       * to generate 4 rgb pixels.
> +       */
> +       while ( frame->curline < cam->height &&
> +               (RingQueue_GetLength(&uvd->dp) >= hor*2)) {
> +               /* get 2 lines of bayer for demosaicing
> +                * into 2 lines of RGB */
> +               RingQueue_Dequeue(&uvd->dp, cam->scratch, hor*2);
> +               bayL0 = (struct bayL0 *) cam->scratch;
> +               bayL1 = (struct bayL1 *) (cam->scratch + hor);
> +               /* frame->curline is the rgb y line */
> +               rgbL0 = (struct rgb *)
> +                               ( frame->data + (cam->width*3*frame->curline));
> +               /* w/2 because we're already doing 2 pixels */
> +               rgbL1 = rgbL0 + (cam->width/2);
> +
> +               for (x=0; x < cam->width; x+=2) {
> +                       rgbL0->r = bayL0->r;
> +                       rgbL0->g = bayL0->g;
> +                       rgbL0->b = bayL1->b;
> +
> +                       rgbL0->r2 = bayL0->r;
> +                       rgbL0->g2 = bayL1->g;
> +                       rgbL0->b2 = bayL1->b;
> +
> +                       rgbL1->r = bayL0->r;
> +                       rgbL1->g = bayL1->g;
> +                       rgbL1->b = bayL1->b;
> +
> +                       rgbL1->r2 = bayL0->r;
> +                       rgbL1->g2 = bayL1->g;
> +                       rgbL1->b2 = bayL1->b;
> +
> +                       rgbL0++;
> +                       rgbL1++;
> +
> +                       bayL0++;
> +                       bayL1++;
> +               }
> +
> +               frame->seqRead_Length += cam->width*3*2;
> +               frame->curline += 2;
> +       }
> +       /* See if we filled the frame */
> +       if (frame->curline == cam->height) {
> +               frame->frameState = FrameState_Done_Hold;
> +               frame->curline = 0;
> +               uvd->curframe = -1;
> +               uvd->stats.frame_num++;
> +       }
> +}
> +
> +/* taken from konicawc */
> +static int qcm_set_video_mode(struct uvd *uvd, struct video_window *vw)
> +{
> +       int ret;
> +       int newsize;
> +       int oldsize;
> +       int x = vw->width;
> +       int y = vw->height;
> +       struct qcm *cam = (struct qcm *) uvd->user_data;
> +
> +       if (x > 0 && y > 0) {
> +               DEBUG(2, "trying to find size %d,%d", x, y);
> +               for (newsize = 0; newsize <= MAX_FRAME_SIZE; newsize++) {
> +                       if ((camera_sizes[newsize].width == x) &&
> +                               (camera_sizes[newsize].height == y))
> +                               break;
> +               }
> +       } else
> +               newsize = cam->size;
> +
> +       if (newsize > MAX_FRAME_SIZE) {
> +               DEBUG(1, "couldn't find size %d,%d", x, y);
> +               return -EINVAL;
> +       }
> +
> +       if (newsize == cam->size) {
> +               DEBUG(1, "Nothing to do");
> +               return 0;
> +       }
> +
> +       qcm_stop_data(uvd);
> +
> +       if (cam->size != newsize) {
> +               oldsize = cam->size;
> +               cam->size = newsize;
> +               ret = qcm_set_camera_size(uvd);
> +               if (ret) {
> +                       err("Couldn't set camera size, err=%d",ret);
> +                       /* restore the original size */
> +                       cam->size = oldsize;
> +                       return ret;
> +               }
> +       }
> +
> +       /* Flush the input queue and clear any current frame in progress */
> +
> +       RingQueue_Flush(&uvd->dp);
> +       if (uvd->curframe != -1) {
> +               uvd->frame[uvd->curframe].curline = 0;
> +               uvd->frame[uvd->curframe].seqRead_Length = 0;
> +               uvd->frame[uvd->curframe].seqRead_Index = 0;
> +       }
> +
> +       CHECK_RET(ret, qcm_start_data(uvd));
> +       return 0;
> +}
> +
> +static int qcm_configure_video(struct uvd *uvd)
> +{
> +       int ret;
> +       memset(&uvd->vpic, 0, sizeof(uvd->vpic));
> +       memset(&uvd->vpic_old, 0x55, sizeof(uvd->vpic_old));
> +
> +       uvd->vpic.colour = colour;
> +       uvd->vpic.hue = hue;
> +       uvd->vpic.brightness = brightness;
> +       uvd->vpic.contrast = contrast;
> +       uvd->vpic.whiteness = whiteness;
> +       uvd->vpic.depth = 24;
> +       uvd->vpic.palette = VIDEO_PALETTE_RGB24;
> +
> +       memset(&uvd->vcap, 0, sizeof(uvd->vcap));
> +       strcpy(uvd->vcap.name, "QCM USB Camera");
> +       uvd->vcap.type = VID_TYPE_CAPTURE;
> +       uvd->vcap.channels = 1;
> +       uvd->vcap.audios = 0;
> +
> +       uvd->vcap.minwidth = camera_sizes[SIZE_160X120].width;
> +       uvd->vcap.minheight = camera_sizes[SIZE_160X120].height;
> +       uvd->vcap.maxwidth = camera_sizes[SIZE_320X240].width;
> +       uvd->vcap.maxheight = camera_sizes[SIZE_320X240].height;
> +
> +       memset(&uvd->vchan, 0, sizeof(uvd->vchan));
> +       uvd->vchan.flags = 0 ;
> +       uvd->vchan.tuners = 0;
> +       uvd->vchan.channel = 0;
> +       uvd->vchan.type = VIDEO_TYPE_CAMERA;
> +       strcpy(uvd->vchan.name, "Camera");
> +
> +       CHECK_RET(ret, qcm_sensor_init(uvd));
> +       return 0;
> +}
> +
> +static int qcm_probe(struct usb_interface *intf,
> +                       const struct usb_device_id *devid)
> +{
> +       int err;
> +       struct uvd *uvd;
> +       struct usb_device *dev = interface_to_usbdev(intf);
> +       struct qcm *cam;
> +       size_t buffer_size;
> +       unsigned char video_ep;
> +       struct usb_host_interface *interface;
> +       struct usb_endpoint_descriptor *endpoint;
> +       int i,j;
> +       unsigned int ifacenum, ifacenum_inact=0;
> +       __le16 sensor_id;
> +
> +       /* we don't support multiconfig cams */
> +       if (dev->descriptor.bNumConfigurations != 1)
> +               return -ENODEV;
> +
> +       /* first check for the video interface and not
> +       * the audio interface */
> +       interface = &intf->cur_altsetting[0];
> +       if ((interface->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC)
> +               || (interface->desc.bInterfaceSubClass !=
> +                       USB_CLASS_VENDOR_SPEC))
> +               return -ENODEV;
> +
> +       /*
> +       walk through each endpoint in each setting in the interface
> +       stop when we find the one that's an isochronous IN endpoint.
> +       */
> +       for (i=0; i < intf->num_altsetting; i++) {
> +               interface = &intf->cur_altsetting[i];
> +               ifacenum = interface->desc.bAlternateSetting;
> +               /* walk the end points */
> +               for (j=0; j < interface->desc.bNumEndpoints; j++) {
> +                       endpoint = &interface->endpoint[j].desc;
> +
> +                       if ((endpoint->bEndpointAddress &
> +                               USB_ENDPOINT_DIR_MASK) != USB_DIR_IN)
> +                               continue; /* not input then not good */
> +
> +                       buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
> +                       if (!buffer_size) {
> +                               ifacenum_inact = ifacenum;
> +                               continue; /* 0 pkt size is not what we want */
> +                       }
> +
> +                       if ((endpoint->bmAttributes &
> +                               USB_ENDPOINT_XFERTYPE_MASK) ==
> +                               USB_ENDPOINT_XFER_ISOC) {
> +                               video_ep = endpoint->bEndpointAddress;
> +                               /* break out of the search */
> +                               goto good_videoep;
> +                       }
> +               }
> +       }
> +       /* failed out since nothing useful was found */
> +       err("No suitable endpoint was found\n");
> +       return -ENODEV;
> +
> +good_videoep:
> +       /* disable isochronous stream before doing anything else */
> +       err = qcm_stv_setb(dev, STV_ISO_ENABLE, 0);
> +       if (err < 0) {
> +               err("Failed to disable sensor stream");
> +               return -EIO;
> +       }
> +
> +       /*
> +       Check that this is the same unknown sensor that is known to work. This
> +       sensor is suspected to be the ST VV6422C001. I'll check the same value
> +       that the qc-usb driver checks. This value is probably not even the
> +       sensor ID since it matches the USB dev ID. Oh well. If it doesn't
> +       match, it's probably a diff sensor so exit and apologize.
> +       */
> +       err = qcm_stv_getw(dev, CMOS_SENSOR_IDREV, &sensor_id);
> +       if (err < 0) {
> +               err("Couldn't read sensor values. Err %d\n",err);
> +               return err;
> +       }
> +       if (sensor_id != cpu_to_le16(0x08F0)) {
> +               err("Sensor ID %x != %x. Unsupported. Sorry\n",
> +                       le16_to_cpu(sensor_id), (0x08F0));
> +               return -ENODEV;
> +       }
> +
> +       uvd = usbvideo_AllocateDevice(cams);
> +       if (!uvd)
> +               return -ENOMEM;
> +
> +       cam = (struct qcm *) uvd->user_data;
> +
> +       /* buf for doing demosaicing */
> +       cam->scratch = kmalloc(324*2, GFP_KERNEL);
> +       if (!cam->scratch) /* uvd freed in dereg */
> +               return -ENOMEM;
> +
> +       /* yes, if we fail after here, cam->scratch gets freed
> +       by qcm_free_uvd */
> +
> +       err = qcm_alloc_int_urb(cam);
> +       if (err < 0)
> +               return err;
> +
> +       /* yes, if we fail after here, int urb gets freed
> +       by qcm_free_uvd */
> +
> +       RESTRICT_TO_RANGE(size, SIZE_160X120, SIZE_320X240);
> +       cam->width = camera_sizes[size].width;
> +       cam->height = camera_sizes[size].height;
> +       cam->size = size;
> +
> +       uvd->debug = debug;
> +       uvd->flags = 0;
> +       uvd->dev = dev;
> +       uvd->iface = intf->altsetting->desc.bInterfaceNumber;
> +       uvd->ifaceAltActive = ifacenum;
> +       uvd->ifaceAltInactive = ifacenum_inact;
> +       uvd->video_endp = video_ep;
> +       uvd->iso_packet_len = buffer_size;
> +       uvd->paletteBits = 1L << VIDEO_PALETTE_RGB24;
> +       uvd->defaultPalette = VIDEO_PALETTE_RGB24;
> +       uvd->canvas = VIDEOSIZE(320, 240);
> +       uvd->videosize = VIDEOSIZE(cam->width, cam->height);
> +       err = qcm_configure_video(uvd);
> +       if (err) {
> +               err("failed to configure video settings");
> +               return err;
> +       }
> +
> +       err = usbvideo_RegisterVideoDevice(uvd);
> +       if (err) { /* the uvd gets freed in Deregister */
> +               err("usbvideo_RegisterVideoDevice() failed.");
> +               return err;
> +       }
> +
> +       uvd->max_frame_size = (320 * 240 * 3);
> +       qcm_register_input(cam, dev);
> +       usb_set_intfdata(intf, uvd);
> +       return 0;
> +}
> +
> +static void qcm_free_uvd(struct uvd *uvd)
> +{
> +       struct qcm *cam = (struct qcm *) uvd->user_data;
> +
> +       kfree(cam->scratch);
> +       qcm_unregister_input(cam);
> +       qcm_free_int(cam);
> +}
> +
> +static struct usbvideo_cb qcm_driver = {
> +       .probe =                qcm_probe,
> +       .setupOnOpen =          qcm_setup_on_open,
> +       .processData =          qcm_process_isoc,
> +       .setVideoMode =         qcm_set_video_mode,
> +       .startDataPump =        qcm_start_data,
> +       .stopDataPump =         qcm_stop_data,
> +       .adjustPicture =        qcm_adjust_picture,
> +       .userFree =             qcm_free_uvd
> +};
> +
> +static int __init qcm_init(void)
> +{
> +       info(DRIVER_DESC " " DRIVER_VERSION);
> +
> +       return usbvideo_register(
> +               &cams,
> +               MAX_CAMERAS,
> +               sizeof(struct qcm),
> +               "QCM",
> +               &qcm_driver,
> +               THIS_MODULE,
> +               qcm_table);
> +}
> +
> +static void __exit qcm_exit(void)
> +{
> +       usbvideo_Deregister(&cams);
> +}
> +
> +module_param(size, int, 0);
> +MODULE_PARM_DESC(size, "Initial Size 0: 160x120 1: 320x240");
> +module_param(colour, int, 0);
> +MODULE_PARM_DESC(colour, "Initial colour");
> +module_param(hue, int, 0);
> +MODULE_PARM_DESC(hue, "Initial hue");
> +module_param(brightness, int, 0);
> +MODULE_PARM_DESC(brightness, "Initial brightness");
> +module_param(contrast, int, 0);
> +MODULE_PARM_DESC(contrast, "Initial contrast");
> +module_param(whiteness, int, 0);
> +MODULE_PARM_DESC(whiteness, "Initial whiteness");
> +
> +#ifdef CONFIG_USB_DEBUG
> +module_param(debug, int, S_IRUGO | S_IWUSR);
> +MODULE_PARM_DESC(debug, "Debug level: 0-9 (default=0)");
> +#endif
> +
> +module_init(qcm_init);
> +module_exit(qcm_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Jaya Kumar");
> +MODULE_DESCRIPTION("QCM USB Camera");
> +MODULE_SUPPORTED_DEVICE("QCM USB Camera");
> diff -X linux-2.6.17-rc4/Documentation/dontdiff -uprN linux-2.6.17-rc4-vanilla/drivers/media/video/usbvideo/quickcam_messenger.h linux-2.6.17-rc4/drivers/media/video/usbvideo/quickcam_messenger.h
> --- linux-2.6.17-rc4-vanilla/drivers/media/video/usbvideo/quickcam_messenger.h  1970-01-01 07:30:00.000000000 +0730
> +++ linux-2.6.17-rc4/drivers/media/video/usbvideo/quickcam_messenger.h  2006-05-17 08:37:05.000000000 +0800
> @@ -0,0 +1,126 @@
> +#ifndef quickcam_messenger_h
> +#define quickcam_messenger_h
> +
> +#ifndef CONFIG_INPUT
> +/* if we're not using input we dummy out these functions */
> +#define qcm_register_input(...)
> +#define qcm_unregister_input(...)
> +#define qcm_report_buttonstat(...)
> +#define qcm_setup_input_int(...) 0
> +#define qcm_stop_int_data(...)
> +#define qcm_alloc_int_urb(...) 0
> +#define qcm_free_int(...)
> +#endif
> +
> +
> +#define CHECK_RET(ret, expr) \
> +       if ((ret = expr) < 0) return ret
> +
> +/* Control Registers for the STVV6422 ASIC
> + * - this define is taken from the qc-usb-messenger code
> + */
> +#define STV_ISO_ENABLE         0x1440
> +#define ISOC_PACKET_SIZE       1023
> +
> +/* Chip identification number including revision indicator */
> +#define CMOS_SENSOR_IDREV      0xE00A
> +
> +struct rgb {
> +       u8 b;
> +       u8 g;
> +       u8 r;
> +       u8 b2;
> +       u8 g2;
> +       u8 r2;
> +};
> +
> +struct bayL0 {
> +#ifdef __BIG_ENDIAN
> +       u8 r;
> +       u8 g;
> +#elif __LITTLE_ENDIAN
> +       u8 g;
> +       u8 r;
> +#else
> +#error not byte order defined
> +#endif
> +};
> +
> +struct bayL1 {
> +#ifdef __BIG_ENDIAN
> +       u8 g;
> +       u8 b;
> +#elif __LITTLE_ENDIAN
> +       u8 b;
> +       u8 g;
> +#else
> +#error not byte order defined
> +#endif
> +};
> +
> +struct cam_size {
> +       u16     width;
> +       u16     height;
> +       u8      cmd;
> +};
> +
> +static const struct cam_size camera_sizes[] = {
> +       { 160, 120, 0xf },
> +       { 320, 240, 0x2 },
> +};
> +
> +enum frame_sizes {
> +       SIZE_160X120    = 0,
> +       SIZE_320X240    = 1,
> +};
> +
> +#define MAX_FRAME_SIZE SIZE_320X240
> +
> +struct qcm {
> +       u16 colour;
> +       u16 hue;
> +       u16 brightness;
> +       u16 contrast;
> +       u16 whiteness;
> +
> +       u8 size;
> +       int height;
> +       int width;
> +       u8 *scratch;
> +       struct urb *button_urb;
> +       u8 button_sts;
> +       u8 button_sts_buf;
> +
> +#ifdef CONFIG_INPUT
> +       struct input_dev *input;
> +       char input_physname[64];
> +#endif
> +};
> +
> +struct regval {
> +       u16 reg;
> +       u8 val;
> +};
> +/* this table is derived from the
> +qc-usb-messenger code */
> +static const struct regval regval_table[] = {
> +       { STV_ISO_ENABLE, 0x00 },
> +       { 0x1436, 0x00 }, { 0x1432, 0x03 },
> +       { 0x143a, 0xF9 }, { 0x0509, 0x38 },
> +       { 0x050a, 0x38 }, { 0x050b, 0x38 },
> +       { 0x050c, 0x2A }, { 0x050d, 0x01 },
> +       { 0x1431, 0x00 }, { 0x1433, 0x34 },
> +       { 0x1438, 0x18 }, { 0x1439, 0x00 },
> +       { 0x143b, 0x05 }, { 0x143c, 0x00 },
> +       { 0x143e, 0x01 }, { 0x143d, 0x00 },
> +       { 0x1442, 0xe2 }, { 0x1500, 0xd0 },
> +       { 0x1500, 0xd0 }, { 0x1500, 0x50 },
> +       { 0x1501, 0xaf }, { 0x1502, 0xc2 },
> +       { 0x1503, 0x45 }, { 0x1505, 0x02 },
> +       { 0x150e, 0x8e }, { 0x150f, 0x37 },
> +       { 0x15c0, 0x00 },
> +};
> +
> +static const unsigned char marker[] = { 0x00, 0xff, 0x00, 0xFF };
> +
> +#endif /* quickcam_messenger_h */
>
>
> -------------------------------------------------------
> Using Tomcat but need to do more? Need to support web services, security?
> Get stuff done quickly with pre-integrated technology to make your job easier
> Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
> http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
> _______________________________________________
> linux-usb-devel@lists.sourceforge.net
> To unsubscribe, use the last form field at:
> https://lists.sourceforge.net/lists/listinfo/linux-usb-devel
>

[ My apologies again. Adding Mauro and video4linux-list to CC list. ]

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [linux-usb-devel] [PATCH/RFC 2.6.17-rc4 1/1] usbvideo/quickcam_messenger driver v4
  2006-05-17  6:14 ` [linux-usb-devel] " Jaya Kumar
@ 2006-05-17 16:35   ` Mauro Carvalho Chehab
  2006-05-17 21:40     ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 5+ messages in thread
From: Mauro Carvalho Chehab @ 2006-05-17 16:35 UTC (permalink / raw)
  To: Jaya Kumar; +Cc: linux-kernel, linux-usb-devel, video4linux-list

Hi, Jaya,


Please send the patches again. All arrived here with a "> " before every
patch line.

Cheers,
Mauro.

Em Qua, 2006-05-17 às 14:14 +0800, Jaya Kumar escreveu:
> [ My apologies again. Adding Mauro and video4linux-list to CC list. ]
> 
> On 5/17/06, jayakumar.video@gmail.com <jayakumar.video@gmail.com> wrote:
> > [ sorry, bungled the l-u-devel list address and subject in prev email ]
> >
> > Hi Greg KH, USB folk, kernel folk,
> >
> > Appended is v4 of my patch adding a usbvideo driver for the Logitech
> > Quickcam Messenger USB webcam. This patch has been rebased against
> > 2.6.17-rc4 as per Greg's feedback.
> >
> > Please let me know if it looks okay and if you have any feedback
> > or suggestions.
> >
> > Thanks,
> > Jaya Kumar
> >
> > Signed-off-by: Jaya Kumar <jayakumar.video@gmail.com>
> >
> > ---
> >
> >  Kconfig              |   12
> >  Makefile             |    1
> >  quickcam_messenger.c | 1120 +++++++++++++++++++++++++++++++++++++++++++++++++++
> >  quickcam_messenger.h |  126 +++++
> >  4 files changed, 1259 insertions(+)
> >
> > ---
> >
> > diff -X linux-2.6.17-rc4/Documentation/dontdiff -uprN linux-2.6.17-rc4-vanilla/drivers/media/video/usbvideo/Kconfig linux-2.6.17-rc4/drivers/media/video/usbvideo/Kconfig
> > --- linux-2.6.17-rc4-vanilla/drivers/media/video/usbvideo/Kconfig       2006-05-17 08:11:36.000000000 +0800
> > +++ linux-2.6.17-rc4/drivers/media/video/usbvideo/Kconfig       2006-05-17 08:39:51.674492008 +0800
> > @@ -36,3 +36,15 @@ config USB_KONICAWC
> >
> >          To compile this driver as a module, choose M here: the
> >          module will be called konicawc.



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [linux-usb-devel] [PATCH/RFC 2.6.17-rc4 1/1] usbvideo/quickcam_messenger driver v4
  2006-05-17 16:35   ` Mauro Carvalho Chehab
@ 2006-05-17 21:40     ` Mauro Carvalho Chehab
  2006-05-17 23:30       ` Jaya Kumar
  0 siblings, 1 reply; 5+ messages in thread
From: Mauro Carvalho Chehab @ 2006-05-17 21:40 UTC (permalink / raw)
  To: Linux and Kernel Video; +Cc: Jaya Kumar, linux-kernel, linux-usb-devel

Em Qua, 2006-05-17 às 13:35 -0300, Mauro Carvalho Chehab escreveu:
> Hi, Jaya,
> 
> 
> Please send the patches again. All arrived here with a "> " before every
> patch line.

Jaya,

This patch I've already applied at the tree (based on the e-mail you've
sent to lkml).
> 
> Cheers,
> Mauro.

Cheers, 
Mauro.


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [linux-usb-devel] [PATCH/RFC 2.6.17-rc4 1/1] usbvideo/quickcam_messenger driver v4
  2006-05-17 21:40     ` Mauro Carvalho Chehab
@ 2006-05-17 23:30       ` Jaya Kumar
  0 siblings, 0 replies; 5+ messages in thread
From: Jaya Kumar @ 2006-05-17 23:30 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Linux and Kernel Video, linux-kernel, linux-usb-devel

On 5/18/06, Mauro Carvalho Chehab <mchehab@infradead.org> wrote:
> Jaya,
>
> This patch I've already applied at the tree (based on the e-mail you've
> sent to lkml).
> >

Thanks Mauro. Yup, it's the same patch. Sorry about the "> ". I got
sloppy with the CC lists.

jaya

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2006-05-17 23:30 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2006-05-17  2:36 [PATCH/RFC 2.6.17-rc4 1/1] usbvideo/quickcam_messenger driver v4 jayakumar.video
2006-05-17  6:14 ` [linux-usb-devel] " Jaya Kumar
2006-05-17 16:35   ` Mauro Carvalho Chehab
2006-05-17 21:40     ` Mauro Carvalho Chehab
2006-05-17 23:30       ` Jaya Kumar

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®