# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher.
# This patch includes the following deltas:
#	           ChangeSet	1.575   -> 1.576  
#	drivers/usb/konicawc.c	1.2     -> 1.3    
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/04/01	spse@secret.org.uk	1.576
# [PATCH] Update to konicawc driver
# 
# This patch against 2.5.7 fixes an oops and a memleak in the konicawc driver
# and also adds an option to set the FPS.
# --------------------------------------------
#
diff -Nru a/drivers/usb/konicawc.c b/drivers/usb/konicawc.c
--- a/drivers/usb/konicawc.c	Wed Apr  3 16:39:12 2002
+++ b/drivers/usb/konicawc.c	Wed Apr  3 16:39:12 2002
@@ -1,5 +1,5 @@
 /*
- * $Id: konicawc.c,v 1.12 2002/02/07 23:18:53 spse Exp $
+ * $Id$
  *
  * konicawc.c - konica webcam driver
  *
@@ -18,17 +18,18 @@
 #include <linux/module.h>
 #include <linux/init.h>
 
-#define DEBUG
-
 #include "usbvideo.h"
 
 #define MAX_BRIGHTNESS	108
 #define MAX_CONTRAST	108
 #define MAX_SATURATION	108
 #define MAX_SHARPNESS	108
-#define MAX_WHITEBAL	363
+#define MAX_WHITEBAL	372
+#define MAX_SPEED	6
+#define MAX_CAMERAS	1
 
-#define MAX_CAMERAS 1
+#define DRIVER_VERSION	"v1.1"
+#define DRIVER_DESC	"Konica Webcam driver"
 
 enum ctrl_req {
 	SetWhitebal	= 0x01,
@@ -40,7 +41,7 @@
 
 
 enum frame_sizes {
-	SIZE_160X130	= 0,
+	SIZE_160X136	= 0,
 	SIZE_176X144	= 1,
 	SIZE_320X240	= 2,
 };
@@ -53,12 +54,30 @@
 
 static int debug;
 static enum frame_sizes size;	
+static int speed = 6;		/* Speed (fps) 0 (slowest) to 6 (fastest) */
 static int brightness =	MAX_BRIGHTNESS/2;
 static int contrast =	MAX_CONTRAST/2;
 static int saturation =	MAX_SATURATION/2;
 static int sharpness =	MAX_SHARPNESS/2;
 static int whitebal =	3*(MAX_WHITEBAL/4);
 
+static int speed_to_interface[] = { 1, 0, 3, 2, 4, 5, 6 };
+
+/* These FPS speeds are from the windows config box. They are
+ * indexed on size (0-2) and speed (0-6). Divide by 3 to get the
+ * real fps.
+ */
+
+static int speed_to_fps[3][7] = { { 24, 40, 48, 60, 72, 80, 100 },
+				  { 18, 30, 36, 45, 54, 60, 75  },
+				  { 6,  10, 12, 15, 18, 20, 25  } };
+
+
+static int camera_sizes[][2] = { { 160, 136 },
+				 { 176, 144 },
+				 { 320, 240 },
+				 { } /* List terminator */
+};
 
 struct konicawc {
 	u8 brightness;		/* camera uses 0 - 9, x11 for real value */
@@ -66,12 +85,12 @@
 	u8 saturation;		/* as above */
 	u8 sharpness;		/* as above */
 	u8 white_bal;		/* 0 - 33, x11 for real value */
-	u8 fps;			/* Stored as fps * 3 */
+	u8 speed;		/* Stored as 0 - 6, used as index in speed_to_* (above) */
 	u8 size;		/* Frame Size */
 	int height;
 	int width;
-	struct urb *sts_urb[USBVIDEO_NUMFRAMES];
-	u8 sts_buf[USBVIDEO_NUMFRAMES][FRAMES_PER_DESC];
+	struct urb *sts_urb[USBVIDEO_NUMSBUF];
+	u8 sts_buf[USBVIDEO_NUMSBUF][FRAMES_PER_DESC];
 	struct urb *last_data_urb;
 	int lastframe;
 };
@@ -97,19 +116,19 @@
 
 	konicawc_set_misc(uvd, 0x2, 0, 0x0b);
 	dbg("setting brightness to %d (%d)", cam->brightness,
-	    cam->brightness*11);
+	    cam->brightness * 11);
 	konicawc_set_value(uvd, cam->brightness, SetBrightness);
 	dbg("setting white balance to %d (%d)", cam->white_bal,
-	    cam->white_bal*11);
+	    cam->white_bal * 11);
 	konicawc_set_value(uvd, cam->white_bal, SetWhitebal);
 	dbg("setting contrast to %d (%d)", cam->contrast,
-	    cam->contrast*11);
-	konicawc_set_value(uvd, cam->brightness, SetBrightness);
+	    cam->contrast * 11);
+	konicawc_set_value(uvd, cam->contrast, SetContrast);
 	dbg("setting saturation to %d (%d)", cam->saturation,
-	    cam->saturation*11);
+	    cam->saturation * 11);
 	konicawc_set_value(uvd, cam->saturation, SetSaturation);
 	dbg("setting sharpness to %d (%d)", cam->sharpness,
-	    cam->sharpness*11);
+	    cam->sharpness * 11);
 	konicawc_set_value(uvd, cam->sharpness, SetSharpness);
 	dbg("setting size %d", cam->size);
 	switch(cam->size) {
@@ -131,6 +150,30 @@
 }
 
 
+static void konicawc_adjust_picture(uvd_t *uvd)
+{
+	struct konicawc *cam = (struct konicawc *)uvd->user_data;
+
+	dbg("new brightness: %d", uvd->vpic.brightness);
+	uvd->vpic.brightness = (uvd->vpic.brightness > MAX_BRIGHTNESS) ? MAX_BRIGHTNESS : uvd->vpic.brightness;
+	if(cam->brightness != uvd->vpic.brightness / 11) {
+	   cam->brightness = uvd->vpic.brightness / 11;
+	   dbg("setting brightness to %d (%d)", cam->brightness,
+	       cam->brightness * 11);
+	   konicawc_set_value(uvd, cam->brightness, SetBrightness);
+	}
+
+	dbg("new contrast: %d", uvd->vpic.contrast);
+	uvd->vpic.contrast = (uvd->vpic.contrast > MAX_CONTRAST) ? MAX_CONTRAST : uvd->vpic.contrast;
+	if(cam->contrast != uvd->vpic.contrast / 11) {
+		cam->contrast = uvd->vpic.contrast / 11;
+		dbg("setting contrast to %d (%d)", cam->contrast,
+		    cam->contrast * 11);
+		konicawc_set_value(uvd, cam->contrast, SetContrast);
+	}
+}
+
+
 static int konicawc_compress_iso(uvd_t *uvd, struct urb *dataurb, struct urb *stsurb)
 {
 	char *cdata;
@@ -138,7 +181,7 @@
 	unsigned char *status = stsurb->transfer_buffer;
 	int keep = 0, discard = 0, bad = 0;
 	static int buttonsts = 0;
-	
+
 	for (i = 0; i < dataurb->number_of_packets; i++) {
 		int button = buttonsts;
 		unsigned char sts;
@@ -228,7 +271,7 @@
 	int i, len = 0;
 	uvd_t *uvd = urb->context;
 	struct konicawc *cam = (struct konicawc *)uvd->user_data;
- 
+
 	/* We don't want to do anything if we are about to be removed! */
 	if (!CAMERA_IS_OPERATIONAL(uvd))
 		return;
@@ -236,7 +279,6 @@
 	if (urb->actual_length > 32) {
 		cam->last_data_urb = urb;
 		return;
-
 	}
 
 	if (!uvd->streaming) {
@@ -244,7 +286,7 @@
 			info("Not streaming, but interrupt!");
 		return;
 	}
-	
+
 	uvd->stats.urb_count++;
 	if (urb->actual_length <= 0)
 		goto urb_done_with;
@@ -329,7 +371,8 @@
 		}
 	}
 
-
+	cam->last_data_urb = NULL;
+	
 	/* Link URBs into a ring so that they invoke each other infinitely */
 	for (i=0; i < USBVIDEO_NUMSBUF; i++) {
 		if ((i+1) < USBVIDEO_NUMSBUF) {
@@ -362,11 +405,14 @@
 static void konicawc_stop_data(uvd_t *uvd)
 {
 	int i, j;
-	struct konicawc *cam = (struct konicawc *)uvd->user_data;
+	struct konicawc *cam;
 
 	if ((uvd == NULL) || (!uvd->streaming) || (uvd->dev == NULL))
 		return;
 
+	cam = (struct konicawc *)uvd->user_data;
+	cam->last_data_urb = NULL;
+
 	/* Unschedule all of the iso td's */
 	for (i=0; i < USBVIDEO_NUMSBUF; i++) {
 		j = usb_unlink_urb(uvd->sbuf[i].urb);
@@ -476,9 +522,9 @@
 static int konicawc_calculate_fps(uvd_t *uvd)
 {
 	struct konicawc *t = uvd->user_data;
-	dbg("");
+	dbg("fps = %d", speed_to_fps[t->size][t->speed]/3);
 
-	return (t->fps)/3;
+	return speed_to_fps[t->size][t->speed]/3;
 }
 
 
@@ -515,10 +561,10 @@
 	uvd->vcap.type = VID_TYPE_CAPTURE;
 	uvd->vcap.channels = 1;
 	uvd->vcap.audios = 0;
-	uvd->vcap.maxwidth = cam->width;
-	uvd->vcap.maxheight = cam->height;
-	uvd->vcap.minwidth = cam->width;
-	uvd->vcap.minheight = cam->height;
+	uvd->vcap.minwidth = camera_sizes[cam->size][0];
+	uvd->vcap.minheight = camera_sizes[cam->size][1];
+	uvd->vcap.maxwidth = camera_sizes[cam->size][0];
+	uvd->vcap.maxheight = camera_sizes[cam->size][1];
 
 	memset(&uvd->vchan, 0, sizeof(uvd->vchan));
 	uvd->vchan.flags = 0 ;
@@ -540,7 +586,7 @@
 }
 
 
-static void *konicawc_probe(struct usb_device *dev, unsigned int ifnum ,const struct usb_device_id *devid)
+static void *konicawc_probe(struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *devid)
 {
 	uvd_t *uvd = NULL;
 	int i, nas;
@@ -555,6 +601,7 @@
 		return NULL;
 
 	info("Konica Webcam (rev. 0x%04x)", dev->descriptor.bcdDevice);
+	RESTRICT_TO_RANGE(speed, 0, MAX_SPEED);
 
 	/* Validate found interface: must have one ISO endpoint */
 	nas = dev->actconfig->interface[ifnum].num_altsetting;
@@ -600,56 +647,58 @@
 				return NULL;
 			}
 		} else {
-			if (actInterface < 0) {
+			if (i == speed_to_interface[speed]) {
+				/* This one is the requested one */
 				actInterface = i;
 				maxPS = endpoint->wMaxPacketSize;
-				if (debug > 0)
-					info("Active setting=%d. maxPS=%d.",
+				if (debug > 0) {
+					info("Selecting requested active setting=%d. maxPS=%d.",
 					     i, maxPS);
-			} else {
-				/* Got another active alt. setting */
-				if (maxPS < endpoint->wMaxPacketSize) {
-					/* This one is better! */
-					actInterface = i;
-					maxPS = endpoint->wMaxPacketSize;
-					if (debug > 0) {
-						info("Even better active setting=%d. maxPS=%d.",
-						     i, maxPS);
-					}
 				}
 			}
 		}
 	}
+	if(actInterface == -1) {
+		err("Cant find required endpoint");
+		return NULL;
+	}
+
 
 	/* Code below may sleep, need to lock module while we are here */
 	MOD_INC_USE_COUNT;
 	uvd = usbvideo_AllocateDevice(cams);
 	if (uvd != NULL) {
-		struct konicawc *konicawc_data = (struct konicawc *)(uvd->user_data);
+		struct konicawc *cam = (struct konicawc *)(uvd->user_data);
 		/* Here uvd is a fully allocated uvd_t object */
-
 		for(i = 0; i < USBVIDEO_NUMSBUF; i++) {
-			konicawc_data->sts_urb[i] = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL);
+			cam->sts_urb[i] = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL);
+			if(cam->sts_urb[i] == NULL) {
+				while(i--) {
+					usb_free_urb(cam->sts_urb[i]);
+				}
+				err("cant allocate urbs");
+				return NULL;
+			}
 		}
-
+		cam->speed = speed;
 		switch(size) {
-		case SIZE_160X130:
+		case SIZE_160X136:
 		default:
-			konicawc_data->height = 136;
-			konicawc_data->width = 160;
-			konicawc_data->size = SIZE_160X130;
+			cam->height = 136;
+			cam->width = 160;
+			cam->size = SIZE_160X136;
 			break;
 
 		case SIZE_176X144:
-			konicawc_data->height = 144;
-			konicawc_data->width = 176;
-			konicawc_data->size = SIZE_176X144;
+			cam->height = 144;
+			cam->width = 176;
+			cam->size = SIZE_176X144;
 			break;
 
 		case SIZE_320X240:
-			konicawc_data->height = 240;
-			konicawc_data->width = 320;
-			konicawc_data->size = SIZE_320X240;
+			cam->height = 240;
+			cam->width = 320;
+			cam->size = SIZE_320X240;
 			break;
 		}
 
@@ -663,14 +712,14 @@
 		uvd->iso_packet_len = maxPS;
 		uvd->paletteBits = 1L << VIDEO_PALETTE_YUV420P;
 		uvd->defaultPalette = VIDEO_PALETTE_YUV420P;
-		uvd->canvas = VIDEOSIZE(konicawc_data->width, konicawc_data->height);
+		uvd->canvas = VIDEOSIZE(cam->width, cam->height);
 		uvd->videosize = uvd->canvas;
 
 		/* Initialize konicawc specific data */
 		konicawc_configure_video(uvd);
 
 		i = usbvideo_RegisterVideoDevice(uvd);
-		uvd->max_frame_size = (konicawc_data->width * konicawc_data->height * 3)/2;
+		uvd->max_frame_size = (cam->width * cam->height * 3)/2;
 		if (i != 0) {
 			err("usbvideo_RegisterVideoDevice() failed.");
 			uvd = NULL;
@@ -681,9 +730,28 @@
 }
 
 
+static void konicawc_free_uvd(uvd_t *uvd)
+{
+	int i;
+	struct konicawc *cam = (struct konicawc *)uvd->user_data;
+
+	for (i=0; i < USBVIDEO_NUMSBUF; i++) {
+		usb_free_urb(cam->sts_urb[i]);
+		cam->sts_urb[i] = NULL;
+	}
+}
+
+
+static struct usb_device_id id_table[] = {
+	{ USB_DEVICE(0x04c8, 0x0720) }, /* Intel YC 76 */
+	{ }  /* Terminating entry */
+};
+
+
 static int __init konicawc_init(void)
 {
 	usbvideo_cb_t cbTbl;
+	info(DRIVER_DESC " " DRIVER_VERSION);
 	memset(&cbTbl, 0, sizeof(cbTbl));
 	cbTbl.probe = konicawc_probe;
 	cbTbl.setupOnOpen = konicawc_setup_on_open;
@@ -691,6 +759,8 @@
 	cbTbl.getFPS = konicawc_calculate_fps;
 	cbTbl.startDataPump = konicawc_start_data;
 	cbTbl.stopDataPump = konicawc_stop_data;
+	cbTbl.adjustPicture = konicawc_adjust_picture;
+	cbTbl.userFree = konicawc_free_uvd;
 	return usbvideo_register(
 		&cams,
 		MAX_CAMERAS,
@@ -706,19 +776,14 @@
 	usbvideo_Deregister(&cams);
 }
 
-#if defined(usb_device_id_ver)
-
-static __devinitdata struct usb_device_id id_table[] = {
-	{ USB_DEVICE(0x04c8, 0x0720) }, /* Intel YC 76 */
-	{ }  /* Terminating entry */
-};
 
 MODULE_DEVICE_TABLE(usb, id_table);
-#endif /* defined(usb_device_id_ver) */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Simon Evans <spse@secret.org.uk>");
-MODULE_DESCRIPTION("Konica Webcam driver");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_PARM(speed, "i");
+MODULE_PARM_DESC(speed, "FPS speed: 0 (slowest) - 6 (fastest)");
 MODULE_PARM(size, "i");
 MODULE_PARM_DESC(size, "Frame Size 0: 160x136 1: 176x144 2: 320x240");
 MODULE_PARM(brightness, "i");
