patch for Ultraport Camera II and 2.6.22.2

Frank Thieme <[email protected]>
Newsgroups gmane.linux.usb.devel
Message-ID <[email protected]>
Hi,

after trying the patch for 2.6.18 I changed it a bit to make it compile with 
2.6.22.2. because kernel is still compiling I didn't test it yet. But if it 
has worked with 2.6.18 it should now, as I didn't any functional changes.

Bye...Frank

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/

_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel
ultracam.patch-2.6.22.2 (text/x-objcsrc, 78.9 KB)
--- linux-2.6.22.2/drivers/media/video/usbvideo/ultracam.c	2007-08-09 23:28:15.000000000 +0200
+++ /usr/src/linux-2.6.22.2/drivers/media/video/usbvideo/ultracam.c	2007-08-12 19:08:49.562471538 +0200
@@ -1,12 +1,16 @@
 /*
- * USB NB Camera driver
+ * USB NB (Ultraport) Camera driver
  *
- * HISTORY:
- * 25-Dec-2002 Dmitri      Removed lighting, sharpness parameters, methods.
+ * Original source by Dmitri
+ * Updated by Karl Gutwin <[email protected]>
+ * Updated by Jakob Lichtenberg <[email protected]>
+ * Kernel version 2.6.10 support by Jeremy Viehland <[email protected]>
+ * Thanks to axel grossklaus <[email protected]>
  */
 
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/moduleparam.h>
 #include <linux/init.h>
 
 #include "usbvideo.h"
@@ -23,21 +27,73 @@
 	int initialized;	/* Had we already sent init sequence? */
 	int camera_model;	/* What type of IBM camera we got? */
 	int has_hdr;
+	int bytes_in;               /* bytes remaining to read before drop */
+ 	int invert;                 /* shall we invert the picture? */
+ 	int rbar, gbar, bbar;       /* mean vals for rgb, used for auto wb */
+ 	int projected_whitebal;
+ 	int frames_since_adjust;
+ 	int ybar;
 } ultracam_t;
 #define	ULTRACAM_T(uvd)	((ultracam_t *)((uvd)->user_data))
 
 static struct usbvideo *cams = NULL;
 
+/*
+ * Intended Debugging Levels
+ *
+ * 0 -- End user messages only.
+ *      Detection of camera.
+ *      Errors which cause camera failure.
+ *
+ * 1 -- End user debugging only.
+ *      Logging of module parameters.
+ *      Camera probe messages.
+ *      Changes to user-configurable settings.
+ *      Automatic changes (e.g., white balance).
+ *      Errors which the user might be able to correct.
+ *
+ * 2 -- Developer messages begin here.
+ *
+ * 3 -- Tracing of interface function entry.
+ *
+ * 4 -- Cruft.
+ *
+ * 5 -- Unused.
+ * 6 -- Unused.
+ * 7 -- Unused.
+ * 8 -- Unused.
+ * 9 -- Unused.
+ */
 static int debug = 0;
+#define TRACE_FUNC_ENTRY() \
+do { if (debug >= 3) dbg("%s", __FUNCTION__); } while (0);
 
-static int flags = 0; /* FLAGS_DISPLAY_HINTS | FLAGS_OVERLAY_STATS; */
+static int ultracam_is_backward(struct uvd* uvd);
+static void ultracam_set_white_balance(struct uvd* uvd);
 
-static const int min_canvasWidth  = 8;
-static const int min_canvasHeight = 4;
+static int flags = 0;
+/* FLAGS_RETRY_VIDIOCSYNC          (1 << 0) /\*   1 *\/ */
+/* FLAGS_MONOCHROME                (1 << 1) /\*   2 *\/ */
+/* FLAGS_DISPLAY_HINTS             (1 << 2) /\*   4 *\/ */
+/* FLAGS_OVERLAY_STATS             (1 << 3) /\*   8 *\/ */
+/* FLAGS_FORCE_TESTPATTERN         (1 << 4) /\*  16 *\/ */
+/* FLAGS_SEPARATE_FRAMES           (1 << 5) /\*  32 *\/ */
+/* FLAGS_CLEAN_FRAMES              (1 << 6) /\*  64 *\/ */
+/* FLAGS_NO_DECODING               (1 << 7) /\* 128 *\/ */
+
+static int const min_canvasWidth = 8;
+static int const min_canvasHeight = 4;
+
+static int whitebal = -1;       /* auto whitebal */
+static int wbmaxdelt = 1;
+
+#define SHARPNESS_MIN   0
+#define SHARPNESS_MAX   4
+static int sharpness = 3;       /* Low noise, good details */
 
 #define FRAMERATE_MIN	0
 #define FRAMERATE_MAX	6
-static int framerate = -1;
+static int framerate = FRAMERATE_MAX;	/* Slow camera. */
 
 /*
  * Here we define several initialization variables. They may
@@ -55,12 +111,52 @@
  */
 static int init_brightness = 128;
 static int init_contrast = 192;
+// TODO: check, if this variable is still needed:
 static int init_color = 128;
+static int init_sat = 10;
 static int init_hue = 128;
+// TODO: check, if this variable is still needed:
 static int hue_correction = 128;
+static int brite = 0;
+module_param(brite, int, 0);
+MODULE_PARM_DESC(brite, "y adjust: (0-255) or -1: automatic (default)");
+
+static unsigned short hs_0b = 0x002e;
+static unsigned short hs_0c = 0x00d6;
+static unsigned short hs_0d = 0x00fc;
+static unsigned short hs_0e = 0x00f1;
+static unsigned short hs_0f = 0x00da;
+static unsigned short hs_10 = 0x0036;
+
+module_param(hs_0b, ushort, 0);
+module_param(hs_0c, ushort, 0);
+module_param(hs_0d, ushort, 0);
+module_param(hs_0e, ushort, 0);
+module_param(hs_0f, ushort, 0);
+module_param(hs_10, ushort, 0);
+
+static unsigned short wb_51 = 0xff00;
+static unsigned short wb_52 = 0xff00;
+static unsigned short wb_53 = 0xff00;
+static unsigned short wb_11 = 0xff00;
+static unsigned short wb_12 = 0xff00;
+static unsigned short wb_13 = 0xff00;
+
+module_param(wb_51, ushort, 0);
+module_param(wb_52, ushort, 0);
+module_param(wb_53, ushort, 0);
+module_param(wb_11, ushort, 0);
+module_param(wb_12, ushort, 0);
+module_param(wb_13, ushort, 0);
+
+static int yuv = 0;
+module_param(yuv, int, 0);
 
 module_param(debug, int, S_IRUGO | S_IWUSR);
+/* MODULE_PARM(debug, "i"); */
+
 MODULE_PARM_DESC(debug, "Debug level: 0-9 (default=0)");
+/* MODULE_PARM(flags, "i"); */
 module_param(flags, int, 0);
 MODULE_PARM_DESC(flags,
 		"Bitfield: 0=VIDIOCSYNC, "
@@ -70,11 +166,23 @@
 		"4=test pattern, "
 		"5=separate frames, "
 		"6=clean frames");
+/* MODULE_PARM(whitebal, "i"); */
+module_param(whitebal, int, 0);
+MODULE_PARM_DESC(whitebal,
+                 "White balance: 0-48 (0:bluish, 48:reddish) or -1: automatic(default)");
+/* MODULE_PARM(wbmaxdelt, "i"); */
+module_param(wbmaxdelt, int, 0);
+MODULE_PARM_DESC(wbmaxdelt,
+                 "White balance maximum delta: 1-48 (1:slow, 48:fast) (default=1)");
+/* MODULE_PARM(sharpness, "i"); */
+module_param(sharpness, int, 0);
+MODULE_PARM_DESC(sharpness, "Sharpness: 0=smooth, 4=sharp (default=3)");
+/* MODULE_PARM(framerate, "i"); */
 module_param(framerate, int, 0);
 MODULE_PARM_DESC(framerate, "Framerate setting: 0=slowest, 6=fastest (default=2)");
 
 module_param(init_brightness, int, 0);
-MODULE_PARM_DESC(init_brightness, "Brightness preconfiguration: 0-255 (default=128)");
+MODULE_PARM_DESC(init_brightness, "Brightness preconfiguration: 0-255 or -1: automatic(default)");
 module_param(init_contrast, int, 0);
 MODULE_PARM_DESC(init_contrast, "Contrast preconfiguration: 0-255 (default=192)");
 module_param(init_color, int, 0);
@@ -84,6 +192,726 @@
 module_param(hue_correction, int, 0);
 MODULE_PARM_DESC(hue_correction, "YUV colorspace regulation: 0-255 (default=128)");
 
+static void
+print_user_data(
+	ultracam_t const* const ultracam)
+{
+	TRACE_FUNC_ENTRY();
+	dbg("ultracam->initialized         = %d", ultracam->initialized);
+	dbg("ultracam->camera_model        = 0x%04x", ultracam->camera_model);
+	dbg("ultracam->has_hdr             = %d", ultracam->has_hdr);
+	dbg("ultracam->bytes_in            = %d", ultracam->bytes_in);
+	dbg("ultracam->invert              = %d", ultracam->invert);
+	dbg("ultracam->rbar                = %d", ultracam->rbar);
+	dbg("ultracam->gbar                = %d", ultracam->gbar);
+	dbg("ultracam->bbar                = %d", ultracam->bbar);
+	dbg("ultracam->projected_whitebal  = %d", ultracam->projected_whitebal);
+	dbg("ultracam->frames_since_adjust = %d", ultracam->frames_since_adjust);
+	dbg("ultracam->ybar                = %d", ultracam->ybar);
+}
+
+/**
+ * Converts given videosize value to readable string.
+ *
+ * @since 07-Aug-2000 Created.
+ * @since 19-Oct-2000 Reworked for usbvideo module.
+ */
+static void usbvideo_VideosizeToString(char* buf, int bufLen, videosize_t vs)
+{
+	char tmp[40];
+	int n;
+	n = 1 + sprintf(tmp, "%ldx%ld", VIDEOSIZE_X(vs), VIDEOSIZE_Y(vs));
+	assert(n < sizeof(tmp));
+	if ((buf == NULL) || (bufLen < n))
+		err("usbvideo_VideosizeToString: buffer is too small.");
+	else
+		memmove(buf, tmp, n);
+}
+
+static void
+print_flags(char* buf, int bufLen, int flags)
+{
+	char* c = buf;
+	int charCount = 0;
+	char const* flagName;
+	int i;
+	int const imax = 7;         /* Number of flags. */
+	assert(NULL != buf);
+	assert(bufLen >= 163*2);    /* 2 * sum(#chars) of all flag names. */
+	buf[bufLen-1] = '\0';
+	for (i = 0; i < imax; ++i)
+	{
+		int flag = 1 << i;
+		if (0 == (flag & flags))
+			continue;           /* Flag not set. */
+		switch (flag)
+		{
+		case FLAGS_RETRY_VIDIOCSYNC:
+			flagName = "FLAGS_RETRY_VIDIOCSYNC";
+			break;
+		case FLAGS_MONOCHROME:
+			flagName = "FLAGS_MONOCHROME";
+			break;
+		case FLAGS_DISPLAY_HINTS:
+			flagName = "FLAGS_DISPLAY_HINTS";
+			break;
+		case FLAGS_OVERLAY_STATS:
+			flagName = "FLAGS_OVERLAY_STATS";
+			break;
+		case FLAGS_FORCE_TESTPATTERN:
+			flagName = "FLAGS_FORCE_TESTPATTERN";
+			break;
+		case FLAGS_SEPARATE_FRAMES:
+			flagName = "FLAGS_SEPARATE_FRAMES";
+			break;
+		case FLAGS_CLEAN_FRAMES:
+			flagName = "FLAGS_CLEAN_FRAMES";
+			break;
+		case FLAGS_NO_DECODING:
+			flagName = "FLAGS_NO_DECODING";
+			break;
+		default:
+			flagName = "Unknown flag";
+			break;
+		}
+		charCount += (sprintf(buf + charCount, "|%s", flagName));
+		c = buf + charCount;
+	}
+	*c = '\0';
+	dbg("uvd->flags             = %s", buf);
+}
+
+static void
+print_uvd(struct uvd const* const uvd)
+{
+	char buf[512];
+	/* struct video_device vdev; */
+	/* struct usb_device* dev; */
+	/* struct usbvideo* handle; */
+	if (NULL != uvd)
+		print_user_data(uvd->user_data);
+	else
+		dbg("uvd->user_data     = NULL");
+	dbg("uvd->user_size         = %d", uvd->user_size);
+	dbg("uvd->debug             = %d", uvd->debug);
+	dbg("uvd->iface             = %d", (int)uvd->iface);
+	dbg("uvd->video_endp        = %d", (int)uvd->video_endp);
+	dbg("uvd->ifaceAltActive    = %d", (int)uvd->ifaceAltActive);
+	dbg("uvd->ifaceAltInactive  = %d", (int)uvd->ifaceAltInactive);
+	print_flags(buf, sizeof(buf), uvd->flags);
+	dbg("uvd->paletteBits       = %08lx", uvd->paletteBits);
+	dbg("uvd->defaultPalette    = %08hx", uvd->defaultPalette);
+	/* struct semaphore lock; */
+	dbg("uvd->user              = %d", uvd->user);
+	usbvideo_VideosizeToString(buf, sizeof(buf), uvd->videosize);
+	dbg("uvd->videosize         = %s", buf);
+	usbvideo_VideosizeToString(buf, sizeof(buf), uvd->canvas);
+	dbg("uvd->canvas            = %s", buf);
+	dbg("uvd->max_frame_size    = %d", uvd->max_frame_size);
+	dbg("uvd->uvd_used          = %d", uvd->uvd_used);
+	dbg("uvd->streaming         = %d", uvd->streaming);
+	dbg("uvd->grabbing          = %d", uvd->grabbing);
+	dbg("uvd->settingsAdjusted  = %d", uvd->settingsAdjusted);
+	dbg("uvd->last_error        = %d", uvd->last_error);
+	/* char* fbuf; */
+	dbg("uvd->fbuf_size         = %d", uvd->fbuf_size);
+	dbg("uvd->curframe          = %d", uvd->curframe);
+	dbg("uvd->iso_packet_len    = %d", uvd->iso_packet_len);
+	/* struct RingQueue dp; */
+	/* struct usbvideo_frame frame[USBVIDEO_NUMFRAMES]; */
+	/* struct usbvideo_sbuf sbuf[USBVIDEO_NUMSBUF]; */
+	dbg("uvd->remove_pending    = %d", uvd->remove_pending);
+	/* struct video_picture vpic, vpic_old; */
+	/* struct video_capability vcap; */
+	/* struct video_channel vchan; */
+	/* struct usbvideo_statistics stats; */
+	dbg("uvd->videoName         = %s", uvd->videoName ? uvd->videoName : "");
+}
+
+/**
+ * Overlays a single character on the frame.  Unsupported characters
+ * will result in a space being printed.
+ *
+ * @todo Support other paletteBits.  Right now, we're only using
+ * RGB24_PUTPIXEL.
+ *
+ * @todo Make overlay more legible by deciding the color of the font
+ * based on the average brightness of the 5x3 block it overlays.  Then
+ * again, this would slow things down.
+ *
+ * @param uvd The device.
+ * @param frame
+ * @param x Where to place the upper left corner of the character.
+ * @param y Where to place the upper left corner of the character.
+ * @param ch Character to print (if not [0-9A-Z], then space).
+ * Lowercase letters will be converted to uppercase.
+ * @since 01-Feb-2000 Created.
+ * @since 31-May-2005 Renovated.
+ */
+void usbvideo_OverlayChar(struct uvd* uvd, struct usbvideo_frame* frame, int x, int y, int ch)
+{
+	/* White letters, 3x5. */
+	static unsigned char const R = 0xff;
+	static unsigned char const G = 0xff;
+	static unsigned char const B = 0xff;
+	static int const overlay_char_sz_x = 3;
+	static int const overlay_char_sz_y = 5;
+	static unsigned short const digits[36] = {
+		0xF6DE, /* 0 */
+		0x2492, /* 1 */
+		0xE7CE, /* 2 */
+		0xE79E, /* 3 */
+		0xB792, /* 4 */
+		0xF39E, /* 5 */
+		0xF3DE, /* 6 */
+		0xF492, /* 7 */
+		0xF7DE, /* 8 */
+		0xF79E, /* 9 */
+		0x77DA, /* a */
+		0xD75C, /* b */
+		0xF24E, /* c */
+		0xD6DC, /* d */
+		0xF34E, /* e */
+		0xF348, /* f */
+		0xf2de, /* g */
+		0xb7da, /* h */
+		0xe92e, /* i */
+		0xe92c, /* j */
+		0xb75a, /* k */
+		0x924e, /* l */
+		0xbeda, /* m */
+		0xd6da, /* n */
+		0x56d4, /* o */
+		0xf7c8, /* p */
+		0xf6fe, /* q */
+		0xf75a, /* r */
+		0x739c, /* s */
+		0xe924, /* t */
+		0xb6de, /* u */
+		0xb6d4, /* v */
+		0xb6fa, /* w */
+		0xb55a, /* x */
+		0xb524, /* y */
+		0xe54e  /* z */
+	};
+	unsigned short digit;
+	int ix, iy;
+
+	if ((uvd == NULL) || (frame == NULL))
+		return;
+
+	if (! (uvd->paletteBits & (1L << VIDEO_PALETTE_RGB24)))
+		return;
+
+	if (ch >= '0' && ch <= '9')
+		ch -= '0';
+	else if (ch >= 'A' && ch <= 'Z')
+		ch = 10 + (ch - 'A');
+	else if (ch >= 'a' && ch <= 'z')
+		ch = 10 + (ch - 'a');
+	else
+		return;                 /* Effectively, prints a space char. */
+	digit = digits[ch];
+
+	{
+		int const xMax = VIDEOSIZE_X(frame->canvas);
+		int const yMax = VIDEOSIZE_Y(frame->canvas);
+		for (iy = 0; iy < overlay_char_sz_y; ++iy)
+			for (ix = 0; ix < overlay_char_sz_x; ++ix, digit <<= 1)
+				if (digit & 0x8000)
+					RGB24_PUTPIXEL(frame,
+						       (x + ix) % xMax,
+						       (y + iy) % yMax,
+						       R, G, B);
+	}
+}
+
+/**
+ * Overlays a string on the frame.  Unsupported characters
+ * will result in a space being printed.
+ *
+ * @param uvd The device.
+ * @param frame
+ * @param x Location of upper left corner of first character.
+ * @param y Location of upper left corner of first character.
+ * @param format Gets passed to snprintf.
+ * @param arg Gets passed to snprintf.
+ * @see usbvideo_OverlayChar
+ * @since 01-Feb-2000 Created.
+ * @since 31-May-2005 Renovated.
+ */
+static void
+usbvideo_OverlayString(struct uvd* uvd, struct usbvideo_frame* frame, int x, int y, char const* format, unsigned long arg)
+{
+	static char buf[16];
+	static int const bufLen = sizeof(buf);
+	char const* str = buf;
+	/* Any mistake in the size of the string will show up on the
+	 * display, so there's no need to check too carefully. */
+	int const nChars = snprintf(buf, bufLen, format, arg);
+	if (nChars >= bufLen)       /* Output was truncated. */
+		buf[bufLen - 1] = '\0'; /* Don't overrun buffer below. */
+	while (*str)
+	{
+		usbvideo_OverlayChar(uvd, frame, x, y, *str);
+		str++;
+		x += 4;                 /* 3 pixels character + 1 space */
+	}
+}
+
+/**
+ * Overlays important debugging information.
+ *
+ * @param uvd The device.
+ * @param frame
+ * @since 01-Feb-2000 Created.
+ * @since 31-May-2005 Renovated.
+ */
+static void
+usbvideo_OverlayStats(struct uvd* uvd, struct usbvideo_frame* frame)
+{
+	ultracam_t* ucam = ULTRACAM_T(uvd);
+	int const y_diff = 8;       /* Vertical separation of lines of overlayed text. */
+	int x = 10, y = 10;
+	long i, j, barLength;
+	int const qi_x1 = 60;
+	int const qi_y1 = 10;
+	int const qi_x2 = VIDEOSIZE_X(frame->request) - 10;
+	int const qi_h = 10;
+	int const xReqLess30 = (VIDEOSIZE_X(frame->request) - 30);
+
+	/*
+	 * Call the user callback, see if we may proceed after that
+	 */
+	if (VALID_CALLBACK(uvd, overlayHook))
+		if (GET_CALLBACK(uvd, overlayHook) (uvd, frame) < 0)
+			return;
+	/**
+	 * Draws a (mostly) hollow rectangle with qi_xxx coordinates.
+	 * Left edge symbolizes the queue index 0; right edge symbolizes
+	 * the full capacity of the queue.
+	 */
+	barLength = qi_x2 - qi_x1 - 2;
+	if ((barLength > 10) && (uvd->paletteBits & (1L << VIDEO_PALETTE_RGB24)))
+	{
+		/* TODO */
+		long u_lo, u_hi, q_used;
+		long m_ri, m_wi, m_lo, m_hi;
+
+		/*
+		 * Determine fill zones (used areas of the queue):
+		 * 0 xxxxxxx u_lo ...... uvd->dp.ri xxxxxxxx u_hi ..... uvd->dp.length
+ 		 *
+		 * if u_lo < 0 then there is no first filler.
+		 */
+
+		q_used = RingQueue_GetLength(&uvd->dp);
+		if ((uvd->dp.ri + q_used) >= uvd->dp.length)
+		{
+			u_hi = uvd->dp.length;
+			u_lo = (q_used + uvd->dp.ri) & (uvd->dp.length - 1);
+		}
+		else
+		{
+			u_hi = (q_used + uvd->dp.ri);
+			u_lo = -1;
+		}
+
+		/*
+		 * Convert byte indices into screen units
+		 */
+		m_ri =               qi_x1 + ((barLength * uvd->dp.ri) / uvd->dp.length);
+		m_wi =               qi_x1 + ((barLength * uvd->dp.wi) / uvd->dp.length);
+		m_lo = (u_lo > 0) ? (qi_x1 + ((barLength * u_lo)       / uvd->dp.length)) : -1;
+		m_hi =               qi_x1 + ((barLength * u_hi)       / uvd->dp.length);
+
+		for (j = qi_y1; j < (qi_y1 + qi_h); j++)
+		{
+			for (i = qi_x1; i < qi_x2; i++)
+			{
+				/*
+				 * Draw border lines
+				 */
+				if ((j == qi_y1) || (j == (qi_y1 + qi_h - 1)) ||
+				    (i == qi_x1) || (i == (qi_x2 - 1)))
+				{
+					RGB24_PUTPIXEL(frame, i, j, 0xFF, 0xFF, 0xFF);
+					continue;
+				}
+				/*
+				 * For all other points the Y coordinate does not matter
+				 */
+				if ((i >= m_ri) && (i <= (m_ri + 3)))
+				{
+					RGB24_PUTPIXEL(frame, i, j, 0x00, 0xFF, 0x00);
+				}
+				else if ((i >= m_wi) && (i <= (m_wi + 3)))
+				{
+					RGB24_PUTPIXEL(frame, i, j, 0xFF, 0x00, 0x00);
+				}
+				else if ((i < m_lo) || ((i > m_ri) && (i < m_hi)))
+					RGB24_PUTPIXEL(frame, i, j, 0x00, 0x00, 0xFF);
+			}
+		}
+	}
+
+	y -= y_diff; /* Back up for the sake of being able to write y += y_diff inline. */
+	/* User has no idea what the rectangle is, so label it `Queue:' */
+	usbvideo_OverlayString(uvd, frame, x, y += y_diff, "Queue:", uvd->stats.frame_num);
+	usbvideo_OverlayString(uvd, frame, x, y += y_diff, "f# %8lx", uvd->stats.frame_num);
+	usbvideo_OverlayString(uvd, frame, x, y += y_diff, "u# %8lx", uvd->stats.urb_count);
+	usbvideo_OverlayString(uvd, frame, x, y += y_diff, "ul %8lx", uvd->stats.urb_length);
+	usbvideo_OverlayString(uvd, frame, x, y += y_diff, "d# %8lx", uvd->stats.data_count);
+	usbvideo_OverlayString(uvd, frame, x, y += y_diff, "h# %8lx", uvd->stats.header_count);
+	usbvideo_OverlayString(uvd, frame, x, y += y_diff, "sk %8lx", uvd->stats.iso_skip_count);
+	usbvideo_OverlayString(uvd, frame, x, y += y_diff, "er %8lx", uvd->stats.iso_err_count);
+	usbvideo_OverlayString(uvd, frame, x, y += y_diff, "wh %8d", uvd->vpic.whiteness >> 8);
+	y += y_diff;                /* Skip a line. */
+	usbvideo_OverlayString(uvd, frame, x, y += y_diff, "br %8x", uvd->vpic.brightness >> 8);
+	usbvideo_OverlayString(uvd, frame, x, y += y_diff, "hu %8x", uvd->vpic.hue);
+	usbvideo_OverlayString(uvd, frame, x, y += y_diff, "cn %8x", uvd->vpic.contrast >> 12);
+	usbvideo_OverlayString(uvd, frame, x, y += y_diff, "co %8x", uvd->vpic.colour);
+	/* On the right hand side, overlay the average color values. */
+	usbvideo_OverlayString(uvd, frame, xReqLess30, 100, "wb %3d", ucam->projected_whitebal);
+	usbvideo_OverlayString(uvd, frame, xReqLess30, 116, "r_ %3d", ucam->rbar);
+	usbvideo_OverlayString(uvd, frame, xReqLess30, 124, "g_ %3d", ucam->gbar);
+	usbvideo_OverlayString(uvd, frame, xReqLess30, 132, "b_ %3d", ucam->bbar);
+	usbvideo_OverlayString(uvd, frame, xReqLess30, 148, "y_ %3d", ucam->ybar);
+}
+
+/**
+ * Does various things to the image once it's been read in, such as
+ * overlay debugging information, and adjust white balance.
+ * @param uvd The device.
+ * @param frame
+ * @since 31-May-2005 Renovated.
+ */
+static void
+ultracam_postProcess(struct uvd* uvd, struct usbvideo_frame* frame)
+{
+	/* This is just _too_ verbose. */
+	/* TRACE_FUNC_ENTRY(); */
+
+	assert(uvd);
+	assert(frame);
+	{
+
+		ultracam_t* const ucam = ULTRACAM_T(uvd);
+		{
+			/* Application wants this size? */
+			int const xRequest = VIDEOSIZE_X(frame->request);
+			int const yRequest = VIDEOSIZE_Y(frame->request);
+			int const xy = xRequest * yRequest;
+			if (!xRequest || !yRequest)
+				return;
+			ucam->rbar /= xy;
+			ucam->gbar /= xy;
+			ucam->bbar /= xy;
+			ucam->ybar /= xy;
+		}
+
+		++ucam->frames_since_adjust;
+		if (ucam->frames_since_adjust > 10)
+		{                           /* module param! */
+			ucam->frames_since_adjust = 0;
+			if (whitebal < 0)
+			{
+				/**
+				 * Auto white balance:
+				 *
+				 * I noticed that on one end of the white balance scale,
+				 * everything looks very blue (low, 0 end). On the other
+				 * end, everything looks very red (high, 48
+				 * end). Therefore, this simple algorithm takes the
+				 * difference between the average red and blue values,
+				 * divides that by two, and changes the white balance by
+				 * that much.
+ *
+				 * delt is the estimated distance between where we are and
+				 * where we need to be, white-balancy speaking. Since
+				 * always jumping by size delt can cause radical color
+				 * shifts (and because I'm a patient guy) we restrict the
+				 * upper range of delt to wbmaxdelt, a module parameter.
+				 * For quicker responses, increase the value of wbmaxdelt.
+				 *
+				 * -Karl
+				 *
+				 * PS. I know next to nothing about color or imaging... if
+				 * someone out there knows a better solution than this,
+				 * let me know! Thanks...
+ */
+				int delt = ((ucam->bbar - ucam->rbar) >> 1);
+
+				if (delt > wbmaxdelt)
+					delt = wbmaxdelt;
+				else if ((delt < 0) && (-delt > wbmaxdelt))
+					delt = -wbmaxdelt;
+
+				ucam->projected_whitebal += delt;
+				RESTRICT_TO_RANGE(ucam->projected_whitebal, 0, 48);
+
+				if (uvd->debug >= 4)
+					info("Autoadjusting whitebal to %d", ucam->projected_whitebal);
+				ultracam_set_white_balance(uvd);
+			}
+		}
+		/* Optionally display statistics on the screen */
+		if (uvd->flags & FLAGS_OVERLAY_STATS)
+			usbvideo_OverlayStats(uvd, frame);
+	}
+}
+
+/*
+ * ultracam_find_header()
+ *
+ * Locate one of supported header markers in the queue.
+ * Once found, remove all preceding bytes AND the marker (8 bytes)
+ * from the data pump queue. Whatever follows must be video lines.
+ *
+ * History:
+ * 5/16/01  Created.
+ */
+static enum ParseState ultracam_find_header(struct uvd* uvd, struct usbvideo_frame* frame)
+{
+	ultracam_t* ucam;
+
+	if ((uvd->curframe) < 0 || (uvd->curframe >= USBVIDEO_NUMFRAMES))
+	{
+		err("ibmcam_find_header: Illegal frame %d.", uvd->curframe);
+		return scan_EndParse;
+	}
+
+	ucam = ULTRACAM_T(uvd);
+	assert(ucam != NULL);
+	ucam->has_hdr = 0;
+	/* and start looking for header */
+
+	while (RingQueue_GetLength(&uvd->dp) >= 8)
+	{
+		if ((RING_QUEUE_PEEK(&uvd->dp, 0) == 0x00) &&
+		    (RING_QUEUE_PEEK(&uvd->dp, 1) == 0x01) &&
+		    (RING_QUEUE_PEEK(&uvd->dp, 2) == 0x19) &&
+		    (RING_QUEUE_PEEK(&uvd->dp, 3) == 0x00) &&
+		    (RING_QUEUE_PEEK(&uvd->dp, 5) == 0x00) &&
+		    (RING_QUEUE_PEEK(&uvd->dp, 6) == 0x00) &&
+		    (RING_QUEUE_PEEK(&uvd->dp, 7) == 0x40))
+		{
+#if 0                           /* This code helps to detect new frame markers */
+			info("Header sig: 00 01 19 00 %02X", RING_QUEUE_PEEK(&uvd->dp, 4));
+#endif
+			frame->header = RING_QUEUE_PEEK(&uvd->dp, 7);
+			if ((frame->header == 0x40))
+			{
+#if 0
+				info("Header found.");
+#endif
+				RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 8);
+				ucam->bytes_in = 7;
+				ucam->has_hdr = 1;
+				ucam->invert = ultracam_is_backward(uvd);
+				ucam->rbar = ucam->gbar = ucam->bbar = 0;
+				break;
+			}
+		}
+		/* If we are still here then this doesn't look like a header */
+		RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 1);
+	}
+
+	if (!ucam->has_hdr)
+	{
+		if (uvd->debug >= 4)
+			info("Skipping frame, no header");
+		return scan_EndParse;
+	}
+
+	/* Header found */
+	ucam->has_hdr = 1;
+	uvd->stats.header_count++;
+	frame->scanstate = ScanState_Lines;
+	frame->curline = 0;
+
+	if (flags & FLAGS_FORCE_TESTPATTERN)
+	{
+		usbvideo_TestPattern(uvd, 1, 1);
+		return scan_NextFrame;
+	}
+	return scan_Continue;
+}
+
+/**
+ * For some unknown reason, the Ultraport camera sticks a fluke byte
+ * into the bytestream every 640 bytes. This produces dotted lines on
+ * the picture, and when left unadjusted, skewed frames.  This routine
+ * simply keeps track of how much we've dequeued so far, and drops the
+ * 640th byte of the stream.  -Karl
+ *
+ * @todo Remove hard-coding of 640, and replace with the size of the
+ * image, to see if that's what the interval truly is.
+ */
+static int ultracam_Dequeue(struct uvd* uvd, unsigned char* buf, int req)
+{
+	static int const nth = 640;
+	int left;
+	ultracam_t* const ucam = ULTRACAM_T(uvd);
+	left = ((nth - 1) - ucam->bytes_in);
+	if (req > left)
+	{
+		RingQueue_Dequeue(&uvd->dp, buf, left);
+		RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 1);
+		ucam->bytes_in = 0;
+		/* mmmm... recursion... <drool> */
+		ultracam_Dequeue(uvd, (buf + left), (req - left));
+	}
+	else
+	{
+		ucam->bytes_in += req;
+		RingQueue_Dequeue(&uvd->dp, buf, req);
+	}
+	return req;
+}
+
+/**
+ * Parses one line (interlaced) from the buffer, put decoded RGB value
+ * into the current frame buffer and add the written number of bytes
+ * (RGB) to the *pcopylen.
+ *
+ * Data seems to look like this:
+ *
+ * <pre>
+ *
+ * |----luma ----|-chroma -|
+ * |-------------+---------|
+ * |YYYYYYYY(320)|UUUU(160)|\
+ * |YYYYYYYY(320)|VVVV(160)| 240 lines
+ * |....     ... |  ...    |/
+ * |_____________|_________|
+ *
+ * </pre>
+ *
+ * The chroma is overlapped over four pixels to fill out the data.
+ *
+ * @since 16-May-2001 Created.
+ */
+static enum ParseState ultracam_parse_lines(struct uvd* uvd, struct usbvideo_frame* frame)
+{
+	ultracam_t* const ucam = ULTRACAM_T(uvd);
+
+	int const xRequest = VIDEOSIZE_X(frame->request); /* Application wants this size? */
+	int const yRequest = VIDEOSIZE_Y(frame->request);
+
+	int const xvid = VIDEOSIZE_X(uvd->videosize); /* We set camera to this resolution? */
+	int const yvid = VIDEOSIZE_Y(uvd->videosize);
+
+	int const ymax = min(yRequest, yvid); /* Use the smaller of the two. */
+	int const xmax = min(xRequest, xvid);
+
+	/**
+	 * We're actually going to process two lines at once.  Each chroma
+	 * byte has to be duplicated over four luma bytes. However, in the
+	 * process of getting the entire chroma set, we end up reading in
+	 * -two- luma lines.  So... we process them all - it's quicker.
+	 * (on my system, this is faster than Windows! :) -Karl
+	 */
+	int const jmax = 2;         /* Parse 2 at a time. */
+	int const tX = 320;
+	int const tX3 = 3 * tX;     /* 2 luma @ 320 + 1 chroma @ 160 */
+
+	unsigned char buf[ tX3 + 10];
+	unsigned char* yl[2];
+	unsigned char* ul;
+	unsigned char* vl;
+	int i, j;
+	unsigned char r, g, b, y, u, v;
+
+	/* Make sure there's enough data for the entire line. */
+	if (RingQueue_GetLength(&uvd->dp) < tX3)
+		return scan_Out;
+
+	/* Suck one line out of the ring queue. */
+	ultracam_Dequeue(uvd, buf, tX3);
+	/* RingQueue_Dequeue(&uvd->dp, buf, tX3); */
+
+	/**
+	 * Make sure that our writing into output buffer will not exceed
+	 * the buffer. Mind that we may write not into current output
+	 * scanline but in several after it as well (if we enlarge image
+	 * vertically.)
+	 */
+	if ((frame->curline + 2) >= ymax)
+		return scan_NextFrame;
+	/**
+	 * Now we are sure that entire line (representing all 'scanLength'
+	 * pixels from the camera) is available in the buffer.  We start
+	 * copying the line left-aligned to the V4L buffer.  If the camera
+	 * line is shorter then we should pad the V4L buffer with
+	 * something (black) to complete the line.
+	 */
+	assert(frame->data != NULL);
+
+	yl[0] = buf;
+	vl = (yl[0] + tX);
+	yl[1] = (vl + (tX >> 1));
+	ul = (yl[1] + tX);
+	for (j = 0; j < jmax; j++)
+	{
+		for (i = 0; i < xmax; i++)
+		{
+			/**
+			 * At some point, I would like to scale the video to fit
+			 * the requested window, rather than ignoring the size
+			 * request.
+			 */
+			if (i < tX)
+			{
+				y = yl[j][i];
+				u = ul[(i >> 1)];
+				v = vl[(i >> 1)];
+				/* invert around 128 (signed/unsigned conflict?) */
+				y += ((y < 128) ? 128 : -128);
+				u += ((u < 128) ? 128 : -128);
+				v += ((v < 128) ? 128 : -128);
+				y += brite;
+			}
+			else
+				y = u = v = 0;
+			if (flags & FLAGS_MONOCHROME)
+			{
+				switch (yuv) {
+				default:
+				case 0: r = g = b = y; break;
+				case 1: r = g = b = u; break;
+				case 2: r = g = b = v; break;
+				}
+			}
+			else
+				YUV_TO_RGB_BY_THE_BOOK(y, u, v, r, g, b);
+			/* this is the slow way to put pixels. if you need speed,
+			 * check out how the IBM cam driver does it.
+			 */
+			if (ucam->invert)
+			{
+				RGB24_PUTPIXEL(frame,
+					       (xmax - i - 1),
+					       (ymax - frame->curline - 3),
+					       r, g, b);
+			}
+			else
+			{
+				RGB24_PUTPIXEL(frame, i, frame->curline, r, g, b);
+			}
+			ucam->ybar += y;
+			ucam->rbar += r;
+			ucam->gbar += g;
+			ucam->bbar += b;
+		}
+		frame->curline++;
+	}
+	if (frame->curline >= ymax)
+		return scan_NextFrame;
+	else
+		return scan_Continue;
+}
+
 /*
  * ultracam_ProcessIsocData()
  *
@@ -96,32 +924,38 @@
  */
 static void ultracam_ProcessIsocData(struct uvd *uvd, struct usbvideo_frame *frame)
 {
-	int n;
+	enum ParseState pstate;
+
+	/* This is just _too_ verbose. */
+	/* TRACE_FUNC_ENTRY(); */
 
 	assert(uvd != NULL);
 	assert(frame != NULL);
 
-	/* Try to move data from queue into frame buffer */
-	n = RingQueue_GetLength(&uvd->dp);
-	if (n > 0) {
-		int m;
-		/* See how much spare we have left */
-		m = uvd->max_frame_size - frame->seqRead_Length;
-		if (n > m)
-			n = m;
-		/* Now move that much data into frame buffer */
-		RingQueue_Dequeue(
-			&uvd->dp,
-			frame->data + frame->seqRead_Length,
-			m);
-		frame->seqRead_Length += m;
-	}
-	/* See if we filled the frame */
-	if (frame->seqRead_Length >= uvd->max_frame_size) {
-		frame->frameState = FrameState_Done;
-		uvd->curframe = -1;
-		uvd->stats.frame_num++;
+	while (1)
+	{
+		pstate = scan_Out;
+		if (frame->scanstate == ScanState_Scanning)
+		{
+			pstate = ultracam_find_header(uvd, frame);
+		}
+		else if (frame->scanstate == ScanState_Lines)
+		{
+			pstate = ultracam_parse_lines(uvd, frame);
+	}
+		if (pstate == scan_Continue)
+			continue;
+		else if ((pstate == scan_NextFrame) || (pstate == scan_Out))
+			break;
+		else
+			return;
 	}
+	if (pstate == scan_NextFrame)
+	{
+ 		frame->frameState = FrameState_Done;
+ 		uvd->curframe = -1;
+ 		uvd->stats.frame_num++;
+ 	}
 }
 
 /*
@@ -135,35 +969,40 @@
 	unsigned char req,
 	unsigned short value,
 	unsigned short index,
-	int is_out)
+	unsigned char *is_out)
 {
 	static const char proc[] = "ultracam_veio";
-	unsigned char cp[8] /* = { 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef } */;
+#if 0
+ 	unsigned char cp[8]
+ 		/* = { 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef } */ ;
+#endif
 	int i;
 
 	if (!CAMERA_IS_OPERATIONAL(uvd))
 		return 0;
 
-	if (!is_out) {
-		i = usb_control_msg(
-			uvd->dev,
+	if (uvd->debug >= 4)
+		dbg("%s(req = 0x%02x, value = 0x%04x, index = 0x%04x",
+		    __FUNCTION__, req, value, index);
+
+	if (is_out) {
+		i = usb_control_msg(uvd->dev,
 			usb_rcvctrlpipe(uvd->dev, 0),
 			req,
 			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
 			value,
 			index,
-			cp,
-			sizeof(cp),
-			1000);
-#if 1
+			is_out,
+			2,
+			HZ);
+#if 0
 		info("USB => %02x%02x%02x%02x%02x%02x%02x%02x "
 		       "(req=$%02x val=$%04x ind=$%04x)",
 		       cp[0],cp[1],cp[2],cp[3],cp[4],cp[5],cp[6],cp[7],
 		       req, value, index);
 #endif
 	} else {
-		i = usb_control_msg(
-			uvd->dev,
+		i = usb_control_msg(uvd->dev,
 			usb_sndctrlpipe(uvd->dev, 0),
 			req,
 			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
@@ -171,7 +1010,7 @@
 			index,
 			NULL,
 			0,
-			1000);
+			HZ);
 	}
 	if (i < 0) {
 		err("%s: ERROR=%d. Camera stopped; Reconnect or reload driver.",
@@ -181,12 +1020,85 @@
 	return i;
 }
 
-/*
- * ultracam_calculate_fps()
+/**
+ * FIXME: This is probably the wrong thing to do in certain
+ * circumstances, because it results in the following call chain:
+ *
+ * <pre>
+ * usb_set_interface     -> usb_disable_interface
+ * usb_disable_interface -> usb_disable_endpoint
+ * usb_disable_endpoint  -> ((struct usb_device*)dev)->bus->op->disable
+ *
+ * NOTE: At /usr/src/linux-2.6.10/drivers/usb/core/hcd.c:1526,
+ *       the above function pointer `disable' is set to
+ *       `hcd_endpoint_disable'.
+ *
+ *                   ... -> hcd_endpoint_disable
+ * hcd_endpoint_disable  -> ...
+ *
+ * NOTE: hcd_endpoint_disable disables the endpoint and
+ *       ***cancels any pending urbs***.
+ * </pre>
+ */
+static int ultracam_alternateSetting(struct uvd* uvd, int setting)
+{
+	static const char proc[] = "ultracam_alternateSetting";
+	int i;
+	if (uvd->debug >= 2)
+		dbg("%s: calling usb_set_interface(dev, uvd->iface = %d, uvd->ifaceAltActive = %d)",
+		    __FUNCTION__, uvd->iface, setting);
+	i = usb_set_interface(uvd->dev, uvd->iface, setting);
+	if (uvd->debug >= 2)
+		dbg("%s: usb_set_interface returned %d", __FUNCTION__, i);
+	if (i < 0)
+	{
+		err("%s: usb_set_interface error", proc);
+		uvd->last_error = i;
+		return -EBUSY;
+	}
+	return 0;
+}
+
+/**
+ * Computes the frame rate.  IBM's website documents that this camera
+ * can do 30 FPS (Frames Per Second).  In reality, don't expect much
+ * from this camera, the number is more like 5.
+ *
+ * @todo Figure out why the original author felt that the framerate
+ * could be a module parameter, and not an intrinsic property of the
+ * camera.
+ *
+ * @todo Figure out where the original framerate computation comes
+ * from, and why <code>fps == 9/2 framerate + 3</code>.  (Note that if
+ * framerate == FRAMERATE_MAX, the above formula yields 30, the
+ * documented maximum value.)
+ *
+ * @return The number of frames per second.
  */
 static int ultracam_calculate_fps(struct uvd *uvd)
 {
-	return 3 + framerate*4 + framerate/2;
+	int fps;
+	TRACE_FUNC_ENTRY();
+	fps = 9/2 * framerate + 3;
+#ifdef DEBUG
+	if (debug >= 1)
+	{
+		static int old_fps = -1;
+		static int const fps_logging_threshold = 1;
+		int const dFPS = (fps - old_fps);
+		if (dFPS >= fps_logging_threshold)
+		{
+			int const increased = (dFPS > 0);
+			dbg("%s: %d frames per second (%s %d)",
+			    __FUNCTION__,
+			    fps,
+			    increased ? "increased by " : "decreased by ",
+			    increased ? dFPS : -dFPS);
+		}
+		old_fps = fps;
+	}
+#endif
+	return fps;
 }
 
 /*
@@ -194,6 +1106,112 @@
  */
 static void ultracam_adjust_contrast(struct uvd *uvd)
 {
+	TRACE_FUNC_ENTRY();
+	if (debug >= 2)
+		info("%s: unimplemented", __FUNCTION__);
+}
+
+/*
+ * ultracam_set_white_balance()
+ */
+static void ultracam_set_white_balance(struct uvd* uvd)
+{
+	int wb;
+	/* white balance codes to send to cam (sniffed from Windows) */
+	unsigned short* wbp;
+	unsigned short whitebalcodes[49 * 6] = {
+		0x0034, 0x003f, 0x00da, 0x00ff, 0x0002, 0x00f5,
+		0x0035, 0x003f, 0x00d6, 0x00ff, 0x0002, 0x00f5,
+		0x0036, 0x003f, 0x00d3, 0x00ff, 0x0002, 0x00f5,
+		0x0037, 0x003f, 0x00cf, 0x00ff, 0x0002, 0x00f5,
+		0x0038, 0x003f, 0x00cc, 0x00ff, 0x0002, 0x00f5,
+		0x0038, 0x003f, 0x00c8, 0x00ff, 0x0002, 0x00f5,
+		0x0039, 0x003f, 0x00c5, 0x00ff, 0x0002, 0x00f5,
+		0x003a, 0x003f, 0x00c1, 0x00ff, 0x0002, 0x00f5,
+		0x003b, 0x003f, 0x00be, 0x0000, 0x0002, 0x00f6,
+		0x003c, 0x003f, 0x00ba, 0x0000, 0x0002, 0x00f6,
+		0x003d, 0x003f, 0x00b6, 0x0000, 0x0002, 0x00f6,
+		0x003e, 0x003f, 0x00b3, 0x0000, 0x0002, 0x00f6,
+		0x003f, 0x003f, 0x00af, 0x0001, 0x0002, 0x00f6,
+		0x003f, 0x003f, 0x00ac, 0x0001, 0x0002, 0x00f6,
+		0x0040, 0x003f, 0x00a8, 0x0001, 0x0002, 0x00f6,
+		0x0041, 0x003f, 0x00a5, 0x0001, 0x0002, 0x00f6,
+		0x0042, 0x003f, 0x00a1, 0x0001, 0x0002, 0x00f6,
+		0x0045, 0x003f, 0x009f, 0x0001, 0x0002, 0x00f6,
+		0x0049, 0x003f, 0x009d, 0x0001, 0x0002, 0x00f6,
+		0x004c, 0x003f, 0x009b, 0x0000, 0x0002, 0x00f7,
+		0x004f, 0x003f, 0x009a, 0x0000, 0x0003, 0x00f7,
+		0x0053, 0x003f, 0x0098, 0x00ff, 0x0003, 0x00f7,
+		0x0056, 0x003f, 0x0096, 0x00ff, 0x0003, 0x00f7,
+		0x0059, 0x003f, 0x0094, 0x00ff, 0x0003, 0x00f7,
+		0x005d, 0x003f, 0x0092, 0x00ff, 0x0003, 0x00f8,
+		0x0060, 0x003f, 0x0090, 0x00ff, 0x0003, 0x00f8,
+		0x0063, 0x003f, 0x008e, 0x00ff, 0x0003, 0x00f8,
+		0x0066, 0x003f, 0x008c, 0x00fe, 0x0003, 0x00f8,
+		0x006a, 0x003f, 0x008b, 0x00fe, 0x0004, 0x00f8,
+		0x006d, 0x003f, 0x0089, 0x00fe, 0x0004, 0x00f8,
+		0x0070, 0x003f, 0x0087, 0x00fe, 0x0004, 0x00f9,
+		0x0074, 0x003f, 0x0085, 0x00fd, 0x0004, 0x00f9,
+		0x0077, 0x003f, 0x0083, 0x00fd, 0x0004, 0x00f9,
+		0x0078, 0x003f, 0x007f, 0x00fd, 0x0004, 0x00f9,
+		0x007a, 0x003f, 0x007b, 0x00fd, 0x0004, 0x00fa,
+		0x007b, 0x003f, 0x0077, 0x00fc, 0x0004, 0x00fa,
+		0x007d, 0x003f, 0x0073, 0x00fc, 0x0004, 0x00fa,
+		0x007e, 0x003f, 0x006f, 0x00fc, 0x0003, 0x00fa,
+		0x007f, 0x003f, 0x006b, 0x00fc, 0x0003, 0x00fb,
+		0x0081, 0x003f, 0x0067, 0x00fb, 0x0003, 0x00fb,
+		0x0082, 0x0040, 0x0064, 0x00fb, 0x0003, 0x00fb,
+		0x0083, 0x0040, 0x0060, 0x00fb, 0x0003, 0x00fb,
+		0x0085, 0x0040, 0x005c, 0x00fb, 0x0003, 0x00fc,
+		0x0086, 0x0040, 0x0058, 0x00fa, 0x0003, 0x00fc,
+		0x0088, 0x0040, 0x0054, 0x00fa, 0x0003, 0x00fc,
+		0x0089, 0x0040, 0x0050, 0x00fa, 0x0002, 0x00fc,
+		0x008a, 0x0040, 0x004c, 0x00fa, 0x0002, 0x00fd,
+		0x008c, 0x0040, 0x0048, 0x00f9, 0x0002, 0x00fd,
+		0x008d, 0x0040, 0x0044, 0x00f9, 0x0002, 0x00fd
+	};
+	/**
+	 * I'm trying to figure out what these codes actually mean. Some
+	 * hints may include rgb or yuv gain within the camera itself, but
+	 * it doesn't make a whole lot of sense, since some values change
+	 * a lot, some hardly change at all, and none is precisely linear
+	 * or even suggests a coordinating function (Yes, I plotted the
+	 * values out. They're not linear.)
+	 *
+	 * Any ideas?
+	 * -Karl
+	 */
+
+	RESTRICT_TO_RANGE(whitebal, -1, 48);
+
+	if (whitebal >= 0)
+		wb = whitebal;
+	else
+		wb = ULTRACAM_T(uvd)->projected_whitebal;
+
+	wbp = whitebalcodes + (wb*6);
+
+	ultracam_veio(uvd, 0x01, ((wb_51 == 0xff00) ? *wbp : wb_51), 0x0051, NULL); wbp++;
+	ultracam_veio(uvd, 0x01, ((wb_52 == 0xff00) ? *wbp : wb_52), 0x0052, NULL); wbp++;
+	ultracam_veio(uvd, 0x01, ((wb_53 == 0xff00) ? *wbp : wb_53), 0x0053, NULL); wbp++;
+	ultracam_veio(uvd, 0x01, ((wb_11 == 0xff00) ? *wbp : wb_11), 0x0011, NULL); wbp++;
+	ultracam_veio(uvd, 0x01, ((wb_12 == 0xff00) ? *wbp : wb_12), 0x0012, NULL); wbp++;
+	ultracam_veio(uvd, 0x01, ((wb_13 == 0xff00) ? *wbp : wb_13), 0x0013, NULL);
+}
+
+/**
+ * Cameras have internal smoothing feature. It is controlled by value
+ * in range [0..4], where 0 is most smooth and 4 is most sharp (raw
+ * image, I guess).  Recommended value is 3.
+ */
+static void ultracam_set_sharpness(struct uvd* uvd)
+{
+	unsigned char buf[2];
+	unsigned short sharps[SHARPNESS_MAX + 1] =
+		{ 0x0004, 0x0024, 0x0064, 0x00a4, 0x00e4 };
+	RESTRICT_TO_RANGE(sharpness, SHARPNESS_MIN, SHARPNESS_MAX);
+	ultracam_veio(uvd, 0x01, 0x0000, 0x0001, buf);
+	ultracam_veio(uvd, 0x01, sharps[sharpness], 0x0001, NULL);
 }
 
 /*
@@ -203,68 +1221,121 @@
  */
 static void ultracam_set_brightness(struct uvd *uvd)
 {
+	TRACE_FUNC_ENTRY();
+	if (debug >= 2)
+		info("%s: unimplemented", __FUNCTION__);
 }
 
 static void ultracam_set_hue(struct uvd *uvd)
 {
+	TRACE_FUNC_ENTRY();
+	if (debug >= 2)
+		info("%s: unimplemented", __FUNCTION__);
 }
 
-/*
- * ultracam_adjust_picture()
- *
- * This procedure gets called from V4L interface to update picture settings.
- * Here we change brightness and contrast.
+/**
+ * This procedure gets called from V4L interface to update picture
+ * settings.  Here we change brightness and contrast.
  */
 static void ultracam_adjust_picture(struct uvd *uvd)
 {
+	TRACE_FUNC_ENTRY();
 	ultracam_adjust_contrast(uvd);
 	ultracam_set_brightness(uvd);
 	ultracam_set_hue(uvd);
 }
 
-/*
- * ultracam_video_stop()
- *
+/**
  * This code tells camera to stop streaming. The interface remains
  * configured and bandwidth - claimed.
  */
 static void ultracam_video_stop(struct uvd *uvd)
 {
+	TRACE_FUNC_ENTRY();
+	/* I think it's this... */
+	ultracam_veio(uvd, 0x02, 0x0000, 0x0001, NULL);
+	ultracam_alternateSetting(uvd, 0x00);
+	ultracam_veio(uvd, 0x01, 0x0000, 0x0001, NULL);
+	ultracam_veio(uvd, 0x02, 0x0003, 0x0000, NULL);
+	ultracam_veio(uvd, 0x02, 0x0000, 0x0005, NULL);
 }
 
-/*
- * ultracam_reinit_iso()
- *
+static int ultracam_resetPipe(struct uvd* uvd)
+{
+	int ret;
+	TRACE_FUNC_ENTRY();
+	/**
+	 * Would like to be able to change the setting (not necessarily
+	 * right here, but `ultracam_alternateSetting' causes the
+	 * isochronous transfer URB's to be cancelled, and doesn't create
+	 * and submit new ones.
+	 */
+	ret = usb_clear_halt(uvd->dev, uvd->video_endp);
+	if (uvd->debug >= 2)
+		if (ret != 0)
+			dbg("%s: usb_clear_halt(uvd->dev, uvd->video_endp = %d) returned %d (ignored)",
+			    __FUNCTION__, (int)uvd->video_endp, ret);
+	return 0;
+}
+
+/**
  * This procedure sends couple of commands to the camera and then
  * resets the video pipe. This sequence was observed to reinit the
  * camera or, at least, to initiate ISO data stream.
  */
 static void ultracam_reinit_iso(struct uvd *uvd, int do_stop)
 {
+	TRACE_FUNC_ENTRY();
+	ultracam_resetPipe(uvd);
 }
 
 static void ultracam_video_start(struct uvd *uvd)
 {
+	TRACE_FUNC_ENTRY();
+	/**
+	 * Perhaps we should implement and call something like:
+	 * ultracam_change_lighting_conditions(uvd);
+	 */
+	/* this is doing no good here... looks like
+	 * sharpness is set on camera init.
+	 ultracam_set_sharpness(uvd);
+	*/
 	ultracam_reinit_iso(uvd, 0);
 }
 
-static int ultracam_resetPipe(struct uvd *uvd)
+/**
+ * This method is called from ultracam_is_backward, and should not be
+ * called from other locations; to do so would defeat the logging of
+ * changes in the camera orientation.
+ *
+ * @return 1 if the camera faces toward the laptop user.
+ */
+static int ultracam_is_forward(struct uvd* uvd)
 {
-	usb_clear_halt(uvd->dev, uvd->video_endp);
-	return 0;
+	unsigned char buf[2];
+	ultracam_veio(uvd, 0x02, 0x0000, 0x000b, buf);
+	return 0x05 == buf[1];
 }
 
-static int ultracam_alternateSetting(struct uvd *uvd, int setting)
+/**
+ * Reports the orientation of the camera.  If debugging is enabled,
+ * logs a message only when the user changes the camera direction.
+ *
+ * @return 1 if the camera faces away from the laptop user.
+ */
+static int ultracam_is_backward(struct uvd* uvd)
 {
-	static const char proc[] = "ultracam_alternateSetting";
-	int i;
-	i = usb_set_interface(uvd->dev, uvd->iface, setting);
-	if (i < 0) {
-		err("%s: usb_set_interface error", proc);
-		uvd->last_error = i;
-		return -EBUSY;
+	static int wasBackward = -1;
+	int const isBackward = ! ultracam_is_forward(uvd);
+	if (debug >= 1)
+	{
+		int const changed = (isBackward != wasBackward);
+		if (changed)
+			info("Camera is facing %s",
+			     isBackward ? "backward" : "forward");
 	}
-	return 0;
+	wasBackward = isBackward;
+	return isBackward;
 }
 
 /*
@@ -272,188 +1343,218 @@
  */
 static int ultracam_setup_on_open(struct uvd *uvd)
 {
+	unsigned char buf[2];
+
 	int setup_ok = 0; /* Success by default */
+
+	TRACE_FUNC_ENTRY();
+
+	ULTRACAM_T(uvd)->projected_whitebal = 20;
+
 	/* Send init sequence only once, it's large! */
-	if (!ULTRACAM_T(uvd)->initialized) {
-		ultracam_alternateSetting(uvd, 0x04);
-		ultracam_alternateSetting(uvd, 0x00);
-		ultracam_veio(uvd, 0x02, 0x0004, 0x000b, 1);
-		ultracam_veio(uvd, 0x02, 0x0001, 0x0005, 1);
-		ultracam_veio(uvd, 0x02, 0x8000, 0x0000, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x0000, 1);
-		ultracam_veio(uvd, 0x00, 0x00b0, 0x0001, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x0002, 1);
-		ultracam_veio(uvd, 0x00, 0x000c, 0x0003, 1);
-		ultracam_veio(uvd, 0x00, 0x000b, 0x0004, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x0005, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x0006, 1);
-		ultracam_veio(uvd, 0x00, 0x0079, 0x0007, 1);
-		ultracam_veio(uvd, 0x00, 0x003b, 0x0008, 1);
-		ultracam_veio(uvd, 0x00, 0x0002, 0x000f, 1);
-		ultracam_veio(uvd, 0x00, 0x0001, 0x0010, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x0011, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00bf, 1);
-		ultracam_veio(uvd, 0x00, 0x0001, 0x00c0, 1);
-		ultracam_veio(uvd, 0x00, 0x0010, 0x00cb, 1);
-		ultracam_veio(uvd, 0x01, 0x00a4, 0x0001, 1);
-		ultracam_veio(uvd, 0x01, 0x0010, 0x0002, 1);
-		ultracam_veio(uvd, 0x01, 0x0066, 0x0007, 1);
-		ultracam_veio(uvd, 0x01, 0x000b, 0x0008, 1);
-		ultracam_veio(uvd, 0x01, 0x0034, 0x0009, 1);
-		ultracam_veio(uvd, 0x01, 0x0000, 0x000a, 1);
-		ultracam_veio(uvd, 0x01, 0x002e, 0x000b, 1);
-		ultracam_veio(uvd, 0x01, 0x00d6, 0x000c, 1);
-		ultracam_veio(uvd, 0x01, 0x00fc, 0x000d, 1);
-		ultracam_veio(uvd, 0x01, 0x00f1, 0x000e, 1);
-		ultracam_veio(uvd, 0x01, 0x00da, 0x000f, 1);
-		ultracam_veio(uvd, 0x01, 0x0036, 0x0010, 1);
-		ultracam_veio(uvd, 0x01, 0x000b, 0x0011, 1);
-		ultracam_veio(uvd, 0x01, 0x0001, 0x0012, 1);
-		ultracam_veio(uvd, 0x01, 0x0000, 0x0013, 1);
-		ultracam_veio(uvd, 0x01, 0x0000, 0x0014, 1);
-		ultracam_veio(uvd, 0x01, 0x0087, 0x0051, 1);
-		ultracam_veio(uvd, 0x01, 0x0040, 0x0052, 1);
-		ultracam_veio(uvd, 0x01, 0x0058, 0x0053, 1);
-		ultracam_veio(uvd, 0x01, 0x0040, 0x0054, 1);
-		ultracam_veio(uvd, 0x01, 0x0000, 0x0040, 1);
-		ultracam_veio(uvd, 0x01, 0x0010, 0x0041, 1);
-		ultracam_veio(uvd, 0x01, 0x0020, 0x0042, 1);
-		ultracam_veio(uvd, 0x01, 0x0030, 0x0043, 1);
-		ultracam_veio(uvd, 0x01, 0x0040, 0x0044, 1);
-		ultracam_veio(uvd, 0x01, 0x0050, 0x0045, 1);
-		ultracam_veio(uvd, 0x01, 0x0060, 0x0046, 1);
-		ultracam_veio(uvd, 0x01, 0x0070, 0x0047, 1);
-		ultracam_veio(uvd, 0x01, 0x0080, 0x0048, 1);
-		ultracam_veio(uvd, 0x01, 0x0090, 0x0049, 1);
-		ultracam_veio(uvd, 0x01, 0x00a0, 0x004a, 1);
-		ultracam_veio(uvd, 0x01, 0x00b0, 0x004b, 1);
-		ultracam_veio(uvd, 0x01, 0x00c0, 0x004c, 1);
-		ultracam_veio(uvd, 0x01, 0x00d0, 0x004d, 1);
-		ultracam_veio(uvd, 0x01, 0x00e0, 0x004e, 1);
-		ultracam_veio(uvd, 0x01, 0x00f0, 0x004f, 1);
-		ultracam_veio(uvd, 0x01, 0x00ff, 0x0050, 1);
-		ultracam_veio(uvd, 0x01, 0x0000, 0x0056, 1);
-		ultracam_veio(uvd, 0x00, 0x0080, 0x00c1, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c2, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0080, 0x00c1, 1);
-		ultracam_veio(uvd, 0x00, 0x0004, 0x00c2, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0002, 0x00c1, 1);
-		ultracam_veio(uvd, 0x00, 0x0020, 0x00c2, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c3, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c4, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c5, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c6, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c7, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c8, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c3, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c4, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c5, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c6, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c7, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c8, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0040, 0x00c1, 1);
-		ultracam_veio(uvd, 0x00, 0x0017, 0x00c2, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c3, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c4, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c5, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c6, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c7, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c8, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c3, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c4, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c5, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c6, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c7, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c8, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
-		ultracam_veio(uvd, 0x00, 0x00c0, 0x00c1, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00c2, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1);
-		ultracam_veio(uvd, 0x02, 0xc040, 0x0001, 1);
-		ultracam_veio(uvd, 0x01, 0x0000, 0x0008, 0);
-		ultracam_veio(uvd, 0x01, 0x0000, 0x0009, 0);
-		ultracam_veio(uvd, 0x01, 0x0000, 0x000a, 0);
-		ultracam_veio(uvd, 0x01, 0x0000, 0x000b, 0);
-		ultracam_veio(uvd, 0x01, 0x0000, 0x000c, 0);
-		ultracam_veio(uvd, 0x01, 0x0000, 0x000d, 0);
-		ultracam_veio(uvd, 0x01, 0x0000, 0x000e, 0);
-		ultracam_veio(uvd, 0x01, 0x0000, 0x000f, 0);
-		ultracam_veio(uvd, 0x01, 0x0000, 0x0010, 0);
-		ultracam_veio(uvd, 0x01, 0x000b, 0x0008, 1);
-		ultracam_veio(uvd, 0x01, 0x0034, 0x0009, 1);
-		ultracam_veio(uvd, 0x01, 0x0000, 0x000a, 1);
-		ultracam_veio(uvd, 0x01, 0x002e, 0x000b, 1);
-		ultracam_veio(uvd, 0x01, 0x00d6, 0x000c, 1);
-		ultracam_veio(uvd, 0x01, 0x00fc, 0x000d, 1);
-		ultracam_veio(uvd, 0x01, 0x00f1, 0x000e, 1);
-		ultracam_veio(uvd, 0x01, 0x00da, 0x000f, 1);
-		ultracam_veio(uvd, 0x01, 0x0036, 0x0010, 1);
-		ultracam_veio(uvd, 0x01, 0x0000, 0x0001, 0);
-		ultracam_veio(uvd, 0x01, 0x0064, 0x0001, 1);
-		ultracam_veio(uvd, 0x01, 0x0059, 0x0051, 1);
-		ultracam_veio(uvd, 0x01, 0x003f, 0x0052, 1);
-		ultracam_veio(uvd, 0x01, 0x0094, 0x0053, 1);
-		ultracam_veio(uvd, 0x01, 0x00ff, 0x0011, 1);
-		ultracam_veio(uvd, 0x01, 0x0003, 0x0012, 1);
-		ultracam_veio(uvd, 0x01, 0x00f7, 0x0013, 1);
-		ultracam_veio(uvd, 0x00, 0x0009, 0x0011, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x0001, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x0000, 1);
-		ultracam_veio(uvd, 0x00, 0x0020, 0x00c1, 1);
-		ultracam_veio(uvd, 0x00, 0x0010, 0x00c2, 1);
-		ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1);
-		ultracam_alternateSetting(uvd, 0x04);
-		ultracam_veio(uvd, 0x02, 0x0000, 0x0001, 1);
-		ultracam_veio(uvd, 0x02, 0x0000, 0x0001, 1);
-		ultracam_veio(uvd, 0x02, 0x0000, 0x0006, 1);
-		ultracam_veio(uvd, 0x02, 0x9000, 0x0007, 1);
-		ultracam_veio(uvd, 0x02, 0x0042, 0x0001, 1);
-		ultracam_veio(uvd, 0x02, 0x0000, 0x000b, 0);
+	if (!ULTRACAM_T(uvd)->initialized)
+	{
+		ultracam_veio(uvd, 0x02, 0x0004, 0x000b, NULL);
+		ultracam_veio(uvd, 0x02, 0x0001, 0x0005, NULL);
+		ultracam_veio(uvd, 0x02, 0x8000, 0x0000, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x0000, NULL);
+		ultracam_veio(uvd, 0x00, 0x00b0, 0x0001, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x0002, NULL);
+		ultracam_veio(uvd, 0x00, 0x000c, 0x0003, NULL);
+		ultracam_veio(uvd, 0x00, 0x000b, 0x0004, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x0005, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x0006, NULL);
+		ultracam_veio(uvd, 0x00, 0x0079, 0x0007, NULL);
+		ultracam_veio(uvd, 0x00, 0x003b, 0x0008, NULL);
+		ultracam_veio(uvd, 0x00, 0x0002, 0x000f, NULL);
+		ultracam_veio(uvd, 0x00, 0x0001, 0x0010, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x0011, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00bf, NULL);
+		ultracam_veio(uvd, 0x00, 0x0001, 0x00c0, NULL);
+		ultracam_veio(uvd, 0x00, 0x0010, 0x00cb, NULL);
+		ultracam_veio(uvd, 0x01, 0x00a4, 0x0001, NULL);
+		ultracam_veio(uvd, 0x01, 0x0010, 0x0002, NULL);
+		ultracam_veio(uvd, 0x01, 0x0066, 0x0007, NULL);
+		ultracam_veio(uvd, 0x01, 0x000b, 0x0008, NULL);
+		ultracam_veio(uvd, 0x01, 0x0034, 0x0009, NULL);
+		ultracam_veio(uvd, 0x01, 0x0000, 0x000a, NULL);
+		ultracam_veio(uvd, 0x01, 0x002e, 0x000b, NULL);
+		ultracam_veio(uvd, 0x01, 0x00d6, 0x000c, NULL);
+		ultracam_veio(uvd, 0x01, 0x00fc, 0x000d, NULL);
+		ultracam_veio(uvd, 0x01, 0x00f1, 0x000e, NULL);
+		ultracam_veio(uvd, 0x01, 0x00da, 0x000f, NULL);
+		ultracam_veio(uvd, 0x01, 0x0036, 0x0010, NULL);
+		ultracam_veio(uvd, 0x01, 0x000b, 0x0011, NULL);
+		ultracam_veio(uvd, 0x01, 0x0001, 0x0012, NULL);
+		ultracam_veio(uvd, 0x01, 0x0000, 0x0013, NULL);
+		ultracam_veio(uvd, 0x01, 0x0000, 0x0014, NULL);
+		ultracam_veio(uvd, 0x01, 0x0087, 0x0051, NULL);
+		ultracam_veio(uvd, 0x01, 0x0040, 0x0052, NULL);
+		ultracam_veio(uvd, 0x01, 0x0058, 0x0053, NULL);
+		ultracam_veio(uvd, 0x01, 0x0040, 0x0054, NULL);
+		ultracam_veio(uvd, 0x01, 0x0000, 0x0040, NULL);
+		ultracam_veio(uvd, 0x01, 0x0010, 0x0041, NULL);
+		ultracam_veio(uvd, 0x01, 0x0020, 0x0042, NULL);
+		ultracam_veio(uvd, 0x01, 0x0030, 0x0043, NULL);
+		ultracam_veio(uvd, 0x01, 0x0040, 0x0044, NULL);
+		ultracam_veio(uvd, 0x01, 0x0050, 0x0045, NULL);
+		ultracam_veio(uvd, 0x01, 0x0060, 0x0046, NULL);
+		ultracam_veio(uvd, 0x01, 0x0070, 0x0047, NULL);
+		ultracam_veio(uvd, 0x01, 0x0080, 0x0048, NULL);
+		ultracam_veio(uvd, 0x01, 0x0090, 0x0049, NULL);
+		ultracam_veio(uvd, 0x01, 0x00a0, 0x004a, NULL);
+		ultracam_veio(uvd, 0x01, 0x00b0, 0x004b, NULL);
+		ultracam_veio(uvd, 0x01, 0x00c0, 0x004c, NULL);
+		ultracam_veio(uvd, 0x01, 0x00d0, 0x004d, NULL);
+		ultracam_veio(uvd, 0x01, 0x00e0, 0x004e, NULL);
+		ultracam_veio(uvd, 0x01, 0x00f0, 0x004f, NULL);
+		ultracam_veio(uvd, 0x01, 0x00ff, 0x0050, NULL);
+		ultracam_veio(uvd, 0x01, 0x0000, 0x0056, NULL);
+		ultracam_veio(uvd, 0x00, 0x0080, 0x00c1, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c2, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0080, 0x00c1, NULL);
+		ultracam_veio(uvd, 0x00, 0x0004, 0x00c2, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0002, 0x00c1, NULL);
+		ultracam_veio(uvd, 0x00, 0x0020, 0x00c2, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c3, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c4, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c5, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c6, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c7, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c8, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c3, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c4, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c5, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c6, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c7, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c8, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0040, 0x00c1, NULL);
+		ultracam_veio(uvd, 0x00, 0x0017, 0x00c2, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c3, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c4, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c5, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c6, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c7, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c8, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c3, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c4, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c5, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c6, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c7, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c8, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, NULL);
+		ultracam_veio(uvd, 0x00, 0x00c0, 0x00c1, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00c2, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, NULL);
+
+		/* command mode? */
+		ultracam_veio(uvd, 0x02, 0xc040, 0x0001, NULL);
+
+		/* read in hue/sat? */
+		ultracam_veio(uvd, 0x01, 0x0000, 0x0008, buf);
+		ultracam_veio(uvd, 0x01, 0x0000, 0x0009, buf);
+		ultracam_veio(uvd, 0x01, 0x0000, 0x000a, buf);
+		ultracam_veio(uvd, 0x01, 0x0000, 0x000b, buf);
+		ultracam_veio(uvd, 0x01, 0x0000, 0x000c, buf);
+		ultracam_veio(uvd, 0x01, 0x0000, 0x000d, buf);
+		ultracam_veio(uvd, 0x01, 0x0000, 0x000e, buf);
+		ultracam_veio(uvd, 0x01, 0x0000, 0x000f, buf);
+		ultracam_veio(uvd, 0x01, 0x0000, 0x0010, buf);
+
+		/* hue/saturation */
+		ultracam_veio(uvd, 0x01, 0x000b, 0x0008, NULL);
+		ultracam_veio(uvd, 0x01, 0x0034, 0x0009, NULL);
+		ultracam_veio(uvd, 0x01, 0x0000, 0x000a, NULL);
+
+		ultracam_veio(uvd, 0x01, hs_0b, 0x000b, NULL);
+		ultracam_veio(uvd, 0x01, hs_0c, 0x000c, NULL);
+		ultracam_veio(uvd, 0x01, hs_0d, 0x000d, NULL);
+		ultracam_veio(uvd, 0x01, hs_0e, 0x000e, NULL);
+		ultracam_veio(uvd, 0x01, hs_0f, 0x000f, NULL);
+		ultracam_veio(uvd, 0x01, hs_10, 0x0010, NULL);
+
+		ultracam_set_sharpness(uvd);
+
+		ultracam_set_white_balance(uvd);
+
+		/* here I have >>>>>>    0x0007  (unknown setting?) */
+		ultracam_veio(uvd, 0x00, 0x0009, 0x0011, NULL);
+
+		ultracam_veio(uvd, 0x00, 0x0000, 0x0001, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x0000, NULL);
+
+		/* on my system, these look like this:
+		 *                 0x00  0x001b  0x00c1
+		 *                 0x00  0x00e0  0x00c2
+		 *                 0x00  0x0000  0x00ca
+		 * some unknown values?
+		 * -Karl
+		 */
+		/* Using Karl's values => green & black image. */
+		ultracam_veio(uvd, 0x00, 0x0020, 0x00c1, NULL);
+		ultracam_veio(uvd, 0x00, 0x0010, 0x00c2, NULL);
+		ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, NULL);
+
+		ultracam_veio(uvd, 0x02, 0x0000, 0x0001, NULL);
+		ultracam_veio(uvd, 0x02, 0x0000, 0x0001, NULL);
+		/* Changing 6 to 7 => no noticeable change. */
+		ultracam_veio(uvd, 0x02, 0x0000, 0x0006, NULL);
+
+		/* resolution */
+		/* How do we get larger frames from camera? */
+		/* Changing 7 to 8 => black image. */
+		ultracam_veio(uvd, 0x02, 0x9000, 0x0007, NULL);
+
+		/* turn on camera? */
+		ultracam_veio(uvd, 0x02, 0x0042, 0x0001, NULL);
+
+		ultracam_is_backward(uvd);
 		ultracam_resetPipe(uvd);
 		ULTRACAM_T(uvd)->initialized = (setup_ok != 0);
+
 	}
 	return setup_ok;
 }
@@ -465,14 +1566,14 @@
 
 	RESTRICT_TO_RANGE(init_brightness, 0, 255);
 	RESTRICT_TO_RANGE(init_contrast, 0, 255);
-	RESTRICT_TO_RANGE(init_color, 0, 255);
-	RESTRICT_TO_RANGE(init_hue, 0, 255);
-	RESTRICT_TO_RANGE(hue_correction, 0, 255);
+	RESTRICT_TO_RANGE(init_sat, 0, 20);
+	RESTRICT_TO_RANGE(init_hue, 0, 360);
+	RESTRICT_TO_RANGE(wbmaxdelt, 1, 48);
 
 	memset(&uvd->vpic, 0, sizeof(uvd->vpic));
 	memset(&uvd->vpic_old, 0x55, sizeof(uvd->vpic_old));
 
-	uvd->vpic.colour = init_color << 8;
+	uvd->vpic.colour = init_sat << 8;
 	uvd->vpic.hue = init_hue << 8;
 	uvd->vpic.brightness = init_brightness << 8;
 	uvd->vpic.contrast = init_contrast << 8;
@@ -481,7 +1582,7 @@
 	uvd->vpic.palette = VIDEO_PALETTE_RGB24;
 
 	memset(&uvd->vcap, 0, sizeof(uvd->vcap));
-	strcpy(uvd->vcap.name, "IBM Ultra Camera");
+	strcpy(uvd->vcap.name, "IBM Ultraport Camera");
 	uvd->vcap.type = VID_TYPE_CAPTURE;
 	uvd->vcap.channels = 1;
 	uvd->vcap.audios = 0;
@@ -498,139 +1599,347 @@
 	strcpy(uvd->vchan.name, "Camera");
 }
 
-/*
- * ultracam_probe()
- *
- * This procedure queries device descriptor and accepts the interface
- * if it looks like our camera.
- *
- * History:
- * 12-Nov-2000 Reworked to comply with new probe() signature.
- * 23-Jan-2001 Added compatibility with 2.2.x kernels.
+/**
+ * Verifies the vendor id and product id of the device.
+ * @return 1 (true) if everything is okay.
  */
-static int ultracam_probe(struct usb_interface *intf, const struct usb_device_id *devid)
+static int
+ultracam_probe_camera_recognized(
+	struct usb_interface* intf,
+	struct usb_device const* const dev)
 {
-	struct usb_device *dev = interface_to_usbdev(intf);
-	struct uvd *uvd = NULL;
-	int ix, i, nas;
-	int actInterface=-1, inactInterface=-1, maxPS=0;
-	unsigned char video_ep = 0;
-
-	if (debug >= 1)
-		info("ultracam_probe(%p)", intf);
-
-	/* We don't handle multi-config cameras */
-	if (dev->descriptor.bNumConfigurations != 1)
-		return -ENODEV;
-
-	info("IBM Ultra camera found (rev. 0x%04x)",
-		le16_to_cpu(dev->descriptor.bcdDevice));
-
-	/* Validate found interface: must have one ISO endpoint */
-	nas = intf->num_altsetting;
-	if (debug > 0)
-		info("Number of alternate settings=%d.", nas);
-	if (nas < 8) {
-		err("Too few alternate settings for this camera!");
-		return -ENODEV;
+	int recognized = 0;
+	if (ULTRACAM_VENDOR_ID != dev->descriptor.idVendor)
+	{
+		err("Wrong vendor id: %d (expected %d).",
+		    dev->descriptor.idVendor,
+		    ULTRACAM_VENDOR_ID);
+	}
+	else if (ULTRACAM_PRODUCT_ID != dev->descriptor.idProduct)
+	{
+		err("Wrong product id: %d (expected %d).",
+		    dev->descriptor.idProduct,
+		    ULTRACAM_PRODUCT_ID);
 	}
-	/* Validate all alternate settings */
-	for (ix=0; ix < nas; ix++) {
-		const struct usb_host_interface *interface;
-		const struct usb_endpoint_descriptor *endpoint;
-
-		interface = &intf->altsetting[ix];
-		i = interface->desc.bAlternateSetting;
-		if (interface->desc.bNumEndpoints != 1) {
+	else if (1 != dev->descriptor.bNumConfigurations)
+	{
+		/** @todo Can't we just ignore all but one? */
+		err("Multi-config cameras not supported.");
+	}
+	else
+	{
+		info("IBM Ultra camera found (rev. 0x%04x)",
+		     dev->descriptor.bcdDevice);
+		recognized = 1;
+	}
+	return recognized;
+}
+
+/**
+ * Interface must have exactly one endpoint.
+ * @todo Document why.
+ * @return 1 on success.
+ */
+int
+ultracam_probe_camera_validate_interface_endpoint_count(
+	struct usb_host_interface const* const interface)
+{
+	int validated_ok =
+		(1 == interface->desc.bNumEndpoints);
+	if (! validated_ok)
+	{
 			err("Interface %d. has %u. endpoints!",
 			    interface->desc.bInterfaceNumber,
 			    (unsigned)(interface->desc.bNumEndpoints));
-			return -ENODEV;
 		}
-		endpoint = &interface->endpoint[0].desc;
-		if (video_ep == 0)
-			video_ep = endpoint->bEndpointAddress;
-		else if (video_ep != endpoint->bEndpointAddress) {
-			err("Alternate settings have different endpoint addresses!");
-			return -ENODEV;
+	return validated_ok;
 		}
-		if ((endpoint->bmAttributes & 0x03) != 0x01) {
+
+/**
+ * Endpoints must be isochronous, and must be inbound.  (Because the
+ * device is a camera, it has a real-time video stream in only one
+ * direction.)
+ *
+ * @return 1 on success.
+ */
+int
+ultracam_probe_camera_validate_interface_endpoint(
+	struct usb_host_interface const* const interface,
+	struct usb_endpoint_descriptor const* endpoint)
+{
+	int validated_ok = 0;
+	if (USB_ENDPOINT_XFER_ISOC !=
+	    (endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK))
+	{
 			err("Interface %d. has non-ISO endpoint!",
 			    interface->desc.bInterfaceNumber);
-			return -ENODEV;
 		}
-		if ((endpoint->bEndpointAddress & 0x80) == 0) {
+	else if (0 ==
+		 (endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK))
+	{
 			err("Interface %d. has ISO OUT endpoint!",
 			    interface->desc.bInterfaceNumber);
+	}
+	else
+	{
+		validated_ok = 1;
+	}
+	return validated_ok;
+}
+
+/**
+ * Validates found interface: must have one ISO endpoint.  Validates
+ * all alternate settings.
+ */
+static int ultracam_probe_camera_validated(
+	struct usb_interface* intf,
+	struct usb_device const* const dev)
+{
+	int i;
+	int iy;
+	unsigned char video_ep = 0;
+	int actInterface = -1;
+	int inactInterface = -1;
+	int maxPS = 0;
+	struct usb_endpoint_descriptor const* endpoint;
+	struct usb_host_interface const* interface;
+	int nas = intf->num_altsetting;
+	for (i = 0; i < nas; i++)
+	{
+		interface = &intf->altsetting[i];
+
+		if (!ultracam_probe_camera_validate_interface_endpoint_count(interface))
+			return -ENODEV;
+
+		endpoint = &interface->endpoint[0].desc;
+
+		if (!ultracam_probe_camera_validate_interface_endpoint(interface, endpoint))
+			return -ENODEV;
+
+		if (0 == video_ep)
+			video_ep = endpoint->bEndpointAddress;
+		else if (video_ep != endpoint->bEndpointAddress)
+		{
+			err("Alternate settings have different endpoint addresses!");
 			return -ENODEV;
 		}
-		if (le16_to_cpu(endpoint->wMaxPacketSize) == 0) {
+
+		iy = interface->desc.bAlternateSetting;
+		if (endpoint->wMaxPacketSize == 0)
+		{
 			if (inactInterface < 0)
-				inactInterface = i;
-			else {
+				inactInterface = iy;
+			else
+			{
 				err("More than one inactive alt. setting!");
 				return -ENODEV;
 			}
-		} else {
-			if (actInterface < 0) {
-				actInterface = i;
-				maxPS = le16_to_cpu(endpoint->wMaxPacketSize);
-				if (debug > 0)
-					info("Active setting=%d. maxPS=%d.", i, maxPS);
-			} else {
-				/* Got another active alt. setting */
-				if (maxPS < le16_to_cpu(endpoint->wMaxPacketSize)) {
-					/* This one is better! */
-					actInterface = i;
-					maxPS = le16_to_cpu(endpoint->wMaxPacketSize);
-					if (debug > 0) {
-						info("Even better ctive setting=%d. maxPS=%d.",
-						     i, maxPS);
-					}
 				}
+		else if ((actInterface < 0) || (maxPS < endpoint->wMaxPacketSize))
+		{
+			actInterface = iy;
+			maxPS = endpoint->wMaxPacketSize;
 			}
 		}
+	if (nas < 8)
+	{
+		if (debug >= 1)
+			info("Number of alternate settings=%d.", nas);
+		err("Too few alternate settings for this camera! (must have >= 8)");
+		return -ENODEV;
 	}
-	if ((maxPS <= 0) || (actInterface < 0) || (inactInterface < 0)) {
+	if ((maxPS <= 0) || (actInterface < 0) || (inactInterface < 0))
+	{
 		err("Failed to recognize the camera!");
 		return -ENODEV;
 	}
+	return 0;
+}
 
-	uvd = usbvideo_AllocateDevice(cams);
-	if (uvd != NULL) {
-		/* Here uvd is a fully allocated uvd object */
-		uvd->flags = flags;
-		uvd->debug = debug;
-		uvd->dev = dev;
-		uvd->iface = intf->altsetting->desc.bInterfaceNumber;
-		uvd->ifaceAltInactive = inactInterface;
-		uvd->ifaceAltActive = actInterface;
-		uvd->video_endp = video_ep;
-		uvd->iso_packet_len = maxPS;
-		uvd->paletteBits = 1L << VIDEO_PALETTE_RGB24;
-		uvd->defaultPalette = VIDEO_PALETTE_RGB24;
-		uvd->canvas = VIDEOSIZE(640, 480);	/* FIXME */
-		uvd->videosize = uvd->canvas; /* ultracam_size_to_videosize(size);*/
-
-		/* Initialize ibmcam-specific data */
-		assert(ULTRACAM_T(uvd) != NULL);
-		ULTRACAM_T(uvd)->camera_model = 0; /* Not used yet */
-		ULTRACAM_T(uvd)->initialized = 0;
-
-		ultracam_configure_video(uvd);
-
-		i = usbvideo_RegisterVideoDevice(uvd);
-		if (i != 0) {
-			err("usbvideo_RegisterVideoDevice() failed.");
-			uvd = NULL;
+static int
+get_actInterface(
+	struct usb_interface const* const intf,
+	struct usb_device const* const dev,
+	int* maxPs)
+{
+	int actInterface = -1;
+	int i;
+	struct usb_endpoint_descriptor const* endpoint;
+	struct usb_host_interface const* interface;
+	int maxPS = 0;
+	int const nas = intf->num_altsetting;
+	int isMaxPsNull = (NULL == maxPs);
+	if (isMaxPsNull)
+		maxPs = &maxPS;
+	for (i = 0; i < nas; i++)
+	{
+		interface = &intf->altsetting[i];
+		endpoint = &interface->endpoint[0].desc;
+		if (0 == endpoint->wMaxPacketSize)
+			continue;
+		if ((actInterface < 0)
+		    ||
+		    (*maxPs < endpoint->wMaxPacketSize))
+		{
+			if (4 == actInterface) /* HACK */
+				continue;       /* Used to use 4, trying to revert to what works. */
+			actInterface = interface->desc.bAlternateSetting;
+			*maxPs = endpoint->wMaxPacketSize;
+			if (! isMaxPsNull && /* Only log once. */
+			    debug > 0)
+				info("Active setting %d: maxPS = %d", actInterface, *maxPs);
 		}
 	}
+	if (actInterface == -1)
+		err("%s: failed to find active interface", __FUNCTION__);
+	return actInterface;
+}
+
+static int
+get_inactInterface(
+	struct usb_interface const* const intf,
+	struct usb_device const* const dev)
+{
+	int inactInterface = -1;
+	int i;
+	struct usb_endpoint_descriptor const* endpoint;
+	struct usb_host_interface const* interface;
+	int const nas = intf->num_altsetting;
+	for (i = 0; i < nas; i++)
+	{
+		interface = &intf->altsetting[i];
+		endpoint = &interface->endpoint[0].desc;
+		if (0 != endpoint->wMaxPacketSize)
+			continue;
+		inactInterface = interface->desc.bAlternateSetting;
+		/* We know that there is exactly one inactive interface
+		 * because we successfully passed
+		 * ultracam_probe_camera_validated.  A failure there would
+		 * have resulted in -ENODEV and a failure of
+		 * ultracam_probe. */
+		break;
+	}
+	/* See comment above, but just in case, we'll report the error. */
+	if (inactInterface == -1)
+		err("%s: failed to find inactive interface", __FUNCTION__);
+	return inactInterface;
+}
+
+static int
+get_maxPS(
+	struct usb_interface const* const intf,
+	struct usb_device const* const dev)
+{
+	int maxPS = 0;
+
+	int const actInterface = get_actInterface(intf, dev, &maxPS);
+
+	if (-1 == actInterface || 0 == maxPS)
+		err("%s: failed to find MaxPacketSize", __FUNCTION__);
+
+	return maxPS;
+}
+
+static unsigned char
+get_video_ep(
+	struct usb_interface const* const intf,
+	struct usb_device const* const dev)
+{
+	unsigned char video_ep = 0;
+
+	/* We can arbitrarily pick 0 because all of the settings are
+	 * verified to be the same by ultracam_probe_camera_validated. */
+	if (0 == video_ep)
+		video_ep = intf->altsetting[0].endpoint[0].desc.bEndpointAddress;
+
+	if (0 == video_ep)
+		err("%s: failed to find video endpoint", __FUNCTION__);
+
+	return video_ep;
+}
+
+/*
+ * ultracam_probe()
+ *
+ * This procedure queries device descriptor and accepts the interface
+ * if it looks like our camera.
+ *
+ * History:
+ * 12-Nov-2000 Reworked to comply with new probe() signature.
+ * 23-Jan-2001 Added compatibility with 2.2.x kernels.
+ */
+static int ultracam_probe(struct usb_interface *intf, const struct usb_device_id *devid)
+{
+	int ret;
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct uvd *uvd = NULL;
+	ultracam_t* ucam = NULL;
+
+	if (debug >= 1)
+		info("ultracam_probe(%p)", intf);
+
+	if (!ultracam_probe_camera_recognized(intf, dev))
+		return -ENODEV;
+
+	if (0 != ultracam_probe_camera_validated(intf, dev))
+		return -ENODEV;
+
+	if (NULL == (uvd = usbvideo_AllocateDevice(cams)))
+		return -EIO;
+
+	/* Here uvd is a fully allocated uvd object */
+	uvd->flags = flags;
+	uvd->debug = debug;
+	uvd->dev = dev;
+	uvd->iface = intf->altsetting->desc.bInterfaceNumber;
+	uvd->ifaceAltInactive = get_inactInterface(intf, dev);
+	uvd->ifaceAltActive = get_actInterface(intf, dev, NULL);
+	uvd->video_endp = get_video_ep(intf, dev);
+	uvd->iso_packet_len = get_maxPS(intf, dev);
+	uvd->paletteBits = 1L << VIDEO_PALETTE_RGB24;
+	uvd->defaultPalette = VIDEO_PALETTE_RGB24;
+	/* uvd->canvas = VIDEOSIZE(320, 240); */
+
+	uvd->canvas = VIDEOSIZE(uvd->iso_packet_len, uvd->iso_packet_len * 3/4);
+	uvd->videosize = uvd->canvas; /* ultracam_size_to_videosize(size);*/
+
+	/* Initialize ibmcam-specific data */
+	ucam                      = ULTRACAM_T(uvd);
+	/* Allocation of user_data field is usbvideo's responsibility. */
+	assert(ucam != NULL);
+	ucam->initialized         = 0;
+	ucam->camera_model        = ULTRACAM_PRODUCT_ID;
+	ucam->bytes_in            = 0;
+	ucam->invert              = ultracam_is_backward(uvd);
+	ucam->rbar                = 0;
+	ucam->gbar                = 0;
+	ucam->bbar                = 0;
+	ucam->projected_whitebal  = 20;
+	ucam->frames_since_adjust = 0;
+	ucam->ybar                = 0;
+
+	ultracam_configure_video(uvd);
+
+	if (0 != (ret = usbvideo_RegisterVideoDevice(uvd)))
+	{
+		err("usbvideo_RegisterVideoDevice failed! (returned %d)", ret);
+		uvd = NULL;
+	}
 
 	if (uvd) {
 		usb_set_intfdata (intf, uvd);
-		return 0;
+		snprintf(uvd->videoName,
+			sizeof(uvd->videoName),
+			"IBM Ultraport Camera (v. %hd.%02hd)",
+			((dev->descriptor.bcdDevice & 0xff00) >> 8),
+			(dev->descriptor.bcdDevice & 0x00ff));
+
+		if (debug >= 3)
+			print_uvd(uvd);
+
 	}
-	return -EIO;
+
+	return ret;
 }
 
 
@@ -639,6 +1948,244 @@
 	{ }  /* Terminating entry */
 };
 
+static int
+ultracam_CompressIsochronous(struct uvd *uvd, struct urb *urb)
+{
+	char *cdata;
+	int i, totlen = 0;
+
+	for (i = 0; i < urb->number_of_packets; i++)
+	{
+		int n = urb->iso_frame_desc[i].actual_length;
+		int st = urb->iso_frame_desc[i].status;
+
+		cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+
+		/*
+		 * Detect and ignore errored packets
+		 */
+		if (st < 0)
+		{
+			if (uvd->debug >= 2)
+				err("Data error: packet=%d. len=%d. status=%d.", i, n, st);
+			uvd->stats.iso_err_count++;
+			continue;
+		}
+
+		/*
+		 * Detect and ignore empty packets
+		 */
+		if (n <= 0)
+		{
+			uvd->stats.iso_skip_count++;
+			continue;
+		}
+		totlen += n;            /* Little local accounting */
+		RingQueue_Enqueue(&uvd->dp, cdata, n);
+	}
+	return totlen;
+}
+
+static void
+ultracam_IsocIrq(struct urb *urb)
+{
+	int i, ret, len;
+	struct uvd *uvd = urb->context;
+
+	/*
+	 * We don't want to do anything if we are about to be removed!
+	 */
+	if (!CAMERA_IS_OPERATIONAL(uvd))
+		return;
+#if 0
+	if (urb->actual_length > 0)
+	{
+		info("urb=$%p status=%d. errcount=%d. length=%d.",
+		     urb, urb->status, urb->error_count, urb->actual_length);
+	}
+	else
+	{
+		static int c = 0;
+		if (c++ % 100 == 0)
+			info("No Isoc data.  (urb=$%p status=%d. errcount=%d. length=%d).",
+			     urb, urb->status, urb->error_count, urb->actual_length);
+	}
+#endif
+
+	if (!uvd->streaming)
+	{
+		if (uvd->debug >= 1)
+			info("Not streaming, but interrupt!");
+		return;
+	}
+
+	uvd->stats.urb_count++;
+	if (urb->actual_length <= 0)
+	{
+		if (uvd->debug >= 2)
+			info("%s: urb->actual_length = %d",
+			     __FUNCTION__, urb->actual_length);
+		goto urb_done_with;
+	}
+
+	/*
+	 * Copy the data received into ring queue
+	 */
+	len = ultracam_CompressIsochronous(uvd, urb);
+	uvd->stats.urb_length = len;
+	if (len <= 0)
+	{
+		if (uvd->debug >= 1)
+			info("%s: len = %d", __FUNCTION__, len);
+		goto urb_done_with;
+	}
+
+	/*
+	 * Here we got some data
+	 */
+	uvd->stats.data_count += len;
+	RingQueue_WakeUpInterruptible(&uvd->dp);
+
+ urb_done_with:
+	for (i = 0; i < FRAMES_PER_DESC; i++)
+	{
+		urb->iso_frame_desc[i].status = 0;
+		urb->iso_frame_desc[i].actual_length = 0;
+	}
+	urb->status = 0;
+	urb->dev = uvd->dev;
+	ret = usb_submit_urb(urb, GFP_KERNEL);
+	if (uvd->debug >= 2)
+		if (ret)
+			err("usb_submit_urb error (%d)", ret);
+	return;
+}
+
+static int
+ultracam_StartDataPump(struct uvd *uvd)
+{
+	struct usb_device *dev = uvd->dev;
+	int i, errFlag;
+
+	if (!CAMERA_IS_OPERATIONAL(uvd))
+	{
+		err("%s: Camera is not operational", __FUNCTION__);
+		return -EFAULT;
+	}
+	uvd->curframe = -1;
+
+	/*
+	 * Alternate interface 1 is is the biggest frame size
+	 */
+	if (uvd->debug >= 3)
+		dbg("%s($%p): enabling data transfers on interfaces %d",
+		    __FUNCTION__, uvd, uvd->ifaceAltActive);
+	i = usb_set_interface(dev, uvd->iface, uvd->ifaceAltActive);
+	if (i < 0)
+	{
+		err("%s: usb_set_interface error: returned %d", __FUNCTION__, i);
+		uvd->last_error = i;
+		return -EBUSY;
+	}
+	if (VALID_CALLBACK(uvd, videoStart))
+		GET_CALLBACK(uvd, videoStart) (uvd);
+	else
+		err("%s: videoStart not set", __FUNCTION__);
+
+	/*
+	 * We double buffer the Iso lists
+	 */
+	for (i = 0; i < USBVIDEO_NUMSBUF; ++i)
+	{
+		int j, k;
+		struct urb *urb             = uvd->sbuf[i].urb;
+		urb->dev                    = dev;
+		urb->context                = uvd;
+		urb->pipe                   = usb_rcvisocpipe(dev, uvd->video_endp);
+		urb->interval               = 2;
+		urb->transfer_flags         = URB_ISO_ASAP;
+		urb->transfer_buffer        = uvd->sbuf[i].data;
+		urb->complete               = ultracam_IsocIrq;
+		urb->number_of_packets      = FRAMES_PER_DESC;
+		urb->transfer_buffer_length = uvd->iso_packet_len * FRAMES_PER_DESC;
+		for (j = k = 0; j < FRAMES_PER_DESC; ++j, k += uvd->iso_packet_len)
+		{
+			urb->iso_frame_desc[j].offset = k;
+			urb->iso_frame_desc[j].length = uvd->iso_packet_len;
+		}
+	}
+
+	/*
+	 * Submit all URBs
+	 */
+	for (i = 0; i < USBVIDEO_NUMSBUF; ++i)
+	{
+		errFlag = usb_submit_urb(uvd->sbuf[i].urb, GFP_KERNEL);
+		if (errFlag)
+			err("%s: usb_submit_urb(%d) ret %d", __FUNCTION__, i, errFlag);
+	}
+
+	uvd->streaming = 1;
+	if (uvd->debug >= 2)
+		info("%s: streaming = %d, video_endp = $%02x",
+		     __FUNCTION__, uvd->streaming, uvd->video_endp);
+	return errFlag;
+}
+
+/*
+ * usbvideo_StopDataPump()
+ *
+ * This procedure stops streaming and deallocates URBs. Then it
+ * activates zero-bandwidth alt. setting of the video interface.
+ *
+ * History:
+ * 22-Jan-2000 Corrected order of actions to work after surprise removal.
+ * 27-Jan-2000 Used uvd->iface, uvd->ifaceAltInactive instead of hardcoded values.
+ */
+static void
+ultracam_StopDataPump(struct uvd *uvd)
+{
+	int i, j;
+
+	if (uvd->debug >= 2)
+		info("%s($%p)", __FUNCTION__, uvd);
+
+	if ((uvd == NULL) || (!uvd->streaming) || (uvd->dev == NULL))
+		return;
+
+	/*
+	 * Unschedule all of the iso td's
+	 */
+	for (i = 0; i < USBVIDEO_NUMSBUF; i++)
+	{
+		usb_kill_urb(uvd->sbuf[i].urb);
+	}
+	if (uvd->debug >= 2)
+		info("%s: streaming=0", __FUNCTION__);
+	uvd->streaming = 0;
+
+	if (!uvd->remove_pending)
+	{
+		/*
+		 * Invoke minidriver's magic to stop the camera
+		 */
+		if (VALID_CALLBACK(uvd, videoStop))
+			GET_CALLBACK(uvd, videoStop) (uvd);
+		else
+			err("%s: videoStop not set", __FUNCTION__);
+
+		/*
+		 * Set packet size to 0
+		 */
+		j = usb_set_interface(uvd->dev, uvd->iface, uvd->ifaceAltInactive);
+		if (j < 0)
+		{
+			err("%s: usb_set_interface() error %d.", __FUNCTION__, j);
+			uvd->last_error = j;
+		}
+	}
+}
+
 /*
  * ultracam_init()
  *
@@ -653,20 +2200,19 @@
 	cbTbl.videoStart = ultracam_video_start;
 	cbTbl.videoStop = ultracam_video_stop;
 	cbTbl.processData = ultracam_ProcessIsocData;
-	cbTbl.postProcess = usbvideo_DeinterlaceFrame;
+	cbTbl.postProcess = ultracam_postProcess;
 	cbTbl.adjustPicture = ultracam_adjust_picture;
 	cbTbl.getFPS = ultracam_calculate_fps;
-	return usbvideo_register(
-		&cams,
+	cbTbl.startDataPump = ultracam_StartDataPump;
+	cbTbl.stopDataPump = ultracam_StopDataPump;
+	return usbvideo_register(&cams,
 		MAX_CAMERAS,
 		sizeof(ultracam_t),
-		"ultracam",
-		&cbTbl,
-		THIS_MODULE,
-		id_table);
+				 "ultracam", &cbTbl, THIS_MODULE, id_table);
 }
 
-static void __exit ultracam_cleanup(void)
+static void __exit
+ultracam_cleanup(void)
 {
 	usbvideo_Deregister(&cams);
 }
signature.asc (application/pgp-signature, 189 B)
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)

iD8DBQBGv0ydOUZy7Ywnge4RApWDAKCierqRL+545XB6blwM9NvZ0acjAwCbBV0B
2kLZLWmVvYlNix8Y7acn6JE=
=X2lV
-----END PGP SIGNATURE-----
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.