## Automatically generated incremental diff
## From:   linux-2.4.11-pre4
## To:     linux-2.4.11-pre5
## Robot:  $Id: make-incremental-diff,v 1.8 2001/12/04 06:05:04 hpa Exp $

diff -urN linux-2.4.11-pre4/CREDITS linux/CREDITS
--- linux-2.4.11-pre4/CREDITS	Mon Dec  3 23:16:27 2001
+++ linux/CREDITS	Mon Dec  3 23:16:37 2001
@@ -844,7 +844,6 @@
 S: USA
 
 N: Johannes Erdfelt
-E: jerdfelt@valinux.com
 E: johannes@erdfelt.com
 D: Linux/IA-64 bootloader and kernel goop, USB
 S: 6350 Stoneridge Mall Road
diff -urN linux-2.4.11-pre4/Documentation/Configure.help linux/Documentation/Configure.help
--- linux-2.4.11-pre4/Documentation/Configure.help	Mon Dec  3 23:16:27 2001
+++ linux/Documentation/Configure.help	Mon Dec  3 23:16:37 2001
@@ -2575,10 +2575,10 @@
   module, say M here and read Documentation/modules.txt. The module
   will be called agpgart.o.
 
-Intel 440LX/BX/GX/815/840/850 support
+Intel 440LX/BX/GX/815/830M/840/850 support
 CONFIG_AGP_INTEL
   This option gives you AGP support for the GLX component of the
-  XFree86 4.x on Intel 440LX/BX/GX, 815, 840 and 850 chipsets.
+  XFree86 4.x on Intel 440LX/BX/GX, 815, 830M, 840 and 850 chipsets.
 
   For the moment, you should probably say N, unless you want to test
   the GLX component for XFree86 3.3.6, which can be downloaded from
@@ -2586,9 +2586,9 @@
 
 Intel I810/I810 DC100/I810e support
 CONFIG_AGP_I810
-  This option gives you AGP support for the Xserver on the Intel 810
-  and 815 chipset boards for their on-board integrated graphics. This
-  is required to do any useful video modes with these boards.
+  This option gives you AGP support for the Xserver on the Intel 810,
+  830M and 815 chipset boards for their on-board integrated graphics.
+  This is required to do any useful video modes with these boards.
 
 VIA chipset support
 CONFIG_AGP_VIA
diff -urN linux-2.4.11-pre4/Documentation/DocBook/Makefile linux/Documentation/DocBook/Makefile
--- linux-2.4.11-pre4/Documentation/DocBook/Makefile	Sun Sep 16 21:23:15 2001
+++ linux/Documentation/DocBook/Makefile	Mon Dec  3 23:16:37 2001
@@ -141,17 +141,18 @@
 OUT	:=	$(patsubst %.sgml, %.out, $(BOOKS))
 
 clean:
-	-$(RM) core *~
-	-$(RM) $(BOOKS)
-	-$(RM) $(DVI) $(AUX) $(TEX) $(LOG) $(OUT)
-	-$(RM) $(JPG-parportbook) $(EPS-parportbook)
-	-$(RM) $(C-procfs-example)
+	rm -f core *~
+	rm -f $(BOOKS)
+	rm -f $(DVI) $(AUX) $(TEX) $(LOG) $(OUT)
+	rm -f $(JPG-parportbook) $(EPS-parportbook)
+	rm -f $(C-procfs-example)
 
 mrproper: clean
-	-$(RM) $(PS) $(PDF)
-	-$(RM) -r $(HTML)
-	-$(RM) .depend
-	-$(RM) $(TOPDIR)/scripts/mkdep-docbook
+	rm -f $(PS) $(PDF)
+	rm -f -r $(HTML)
+	rm -f .depend
+	rm -f $(TOPDIR)/scripts/mkdep-docbook
+	rm -rf DBTOHTML_OUTPUT*
 
 %.ps : %.sgml
 	@(which db2ps > /dev/null 2>&1) || \
@@ -169,7 +170,7 @@
 	@(which db2html > /dev/null 2>&1) || \
 	 (echo "*** You need to install DocBook stylesheets ***"; \
 	  exit 1)
-	-$(RM) -r $@
+	rm -rf $@
 	db2html $<
 	if [ ! -z "$(JPG-$@)" ]; then cp $(JPG-$@) $@; fi
 
diff -urN linux-2.4.11-pre4/Documentation/DocBook/kernel-api.tmpl linux/Documentation/DocBook/kernel-api.tmpl
--- linux-2.4.11-pre4/Documentation/DocBook/kernel-api.tmpl	Sun Sep  9 12:00:55 2001
+++ linux/Documentation/DocBook/kernel-api.tmpl	Mon Dec  3 23:16:37 2001
@@ -187,11 +187,6 @@
 !Edrivers/block/ll_rw_blk.c
   </chapter>
 
-  <chapter id="part">
-     <title>Partition Handling</title>
-!Edrivers/block/genhd.c
-  </chapter>
-
   <chapter id="miscdev">
      <title>Miscellaneous Devices</title>
 !Edrivers/char/misc.c
diff -urN linux-2.4.11-pre4/Documentation/DocBook/kernel-hacking.tmpl linux/Documentation/DocBook/kernel-hacking.tmpl
--- linux-2.4.11-pre4/Documentation/DocBook/kernel-hacking.tmpl	Sat May 19 17:43:05 2001
+++ linux/Documentation/DocBook/kernel-hacking.tmpl	Mon Dec  3 23:16:37 2001
@@ -976,17 +976,35 @@
    </para>
   </sect1>
 
-  <sect1 id="sym-exportsymtab">
-   <title><function>EXPORT_SYMTAB</function></title>
+  <sect1 id="sym-exportnosymbols">
+   <title><symbol>EXPORT_NO_SYMBOLS</symbol>
+    <filename class=headerfile>include/linux/module.h</filename></title>
+
+   <para>
+    If a module exports no symbols then you can specify
+    <programlisting>
+EXPORT_NO_SYMBOLS;
+    </programlisting>
+    anywhere in the module.
+    In kernel 2.4 and earlier, if a module contains neither
+    <function>EXPORT_SYMBOL()</function> nor
+    <symbol>EXPORT_NO_SYMBOLS</symbol> then the module defaults to
+    exporting all non-static global symbols.
+    In kernel 2.5 onwards you must explicitly specify whether a module
+    exports symbols or not.
+   </para>
+  </sect1>
 
-   <para>
-    For convenience, a module usually exports all non-file-scope
-    symbols (ie. all those not declared <type>static</type>).  If this
-    is defined before
+  <sect1 id="sym-exportsymbols-gpl">
+   <title><function>EXPORT_SYMBOL_GPL()</function>
+    <filename class=headerfile>include/linux/module.h</filename></title>
 
-    <filename class=headerfile>include/linux/module.h</filename> is
-    included, then only symbols explicit exported with
-    <function>EXPORT_SYMBOL()</function> will be exported.
+   <para>
+    Similar to <function>EXPORT_SYMBOL()</function> except that the
+    symbols exported by <function>EXPORT_SYMBOL_GPL()</function> can
+    only be seen by modules with a
+    <function>MODULE_LICENCE()</function> that specifies a GPL
+    compatible license.
    </para>
   </sect1>
  </chapter>
@@ -1241,9 +1259,19 @@
      Edit the <filename>Makefile</filename>: the CONFIG variables are
      exported here so you can conditionalize compilation with `ifeq'.
      If your file exports symbols then add the names to
-     <varname>MX_OBJS</varname> or <varname>OX_OBJS</varname> instead
-     of <varname>M_OBJS</varname> or <varname>O_OBJS</varname>, so
-     that genksyms will find them.
+     <varname>export-objs</varname> so that genksyms will find them.
+     <caution>
+      <para>
+       There is a restriction on the kernel build system that objects
+       which export symbols must have globally unique names.
+       If your object does not have a globally unique name then the
+       standard fix is to move the
+       <function>EXPORT_SYMBOL()</function> statements to their own
+       object with a unique name.
+       This is why several systems have separate exporting objects,
+       usually suffixed with ksyms.
+      </para>
+     </caution>
     </para>
    </listitem>
 
diff -urN linux-2.4.11-pre4/Documentation/DocBook/tulip-user.tmpl linux/Documentation/DocBook/tulip-user.tmpl
--- linux-2.4.11-pre4/Documentation/DocBook/tulip-user.tmpl	Wed Apr 18 14:40:06 2001
+++ linux/Documentation/DocBook/tulip-user.tmpl	Mon Dec  3 23:16:37 2001
@@ -90,7 +90,7 @@
 
   </chapter>
 
-  <chapter id="drvr_compat">
+  <chapter id="drvr-compat">
     <title>Driver Compatibility</title>
 
 <para>
@@ -111,7 +111,7 @@
 </para>
   </chapter>
 
-  <chapter id="board_settings">
+  <chapter id="board-settings">
     <title>Board-specific Settings</title>
 
 <para>
@@ -129,7 +129,7 @@
 </para>
   </chapter>
 
-  <chapter id="driver_operation">
+  <chapter id="driver-operation">
     <title>Driver Operation</title>
 
 <sect1><title>Ring buffers</title>
diff -urN linux-2.4.11-pre4/Documentation/usb/CREDITS linux/Documentation/usb/CREDITS
--- linux-2.4.11-pre4/Documentation/usb/CREDITS	Mon Oct  2 12:02:16 2000
+++ linux/Documentation/usb/CREDITS	Mon Dec  3 23:16:37 2001
@@ -8,7 +8,7 @@
   David Brownell <dbrownell@users.sourceforge.net>
   Alan Cox <alan@lxorguk.ukuu.org.uk>
   Randy Dunlap <randy.dunlap@intel.com>
-  Johannes Erdfelt <jerdfelt@sventech.com>
+  Johannes Erdfelt <johannes@erdfelt.com>
   Deti Fliegl <deti@fliegl.de>
   ham <ham@unsuave.com>
   Bradley M Keryan <keryan@andrew.cmu.edu>
diff -urN linux-2.4.11-pre4/MAINTAINERS linux/MAINTAINERS
--- linux-2.4.11-pre4/MAINTAINERS	Mon Dec  3 23:16:27 2001
+++ linux/MAINTAINERS	Mon Dec  3 23:16:37 2001
@@ -337,6 +337,12 @@
 M:	hpa@zytor.com
 S:	Maintained
 
+CRAMFS FILESYSTEM
+P:	Daniel Quinlan
+M:	quinlan@transmeta.com
+W:	http://sourceforge.net/projects/cramfs/
+S:	Maintained
+
 CREDITS FILE
 P:	John A. Martin
 M:	jam@acm.org
@@ -1481,7 +1487,6 @@
 
 USB HUB
 P:	Johannes Erdfelt
-M:	jerdfelt@valinux.com
 M:	johannes@erdfelt.com
 L:	linux-usb-users@lists.sourceforge.net
 L:	linux-usb-devel@lists.sourceforge.net
@@ -1586,7 +1591,6 @@
 
 USB SUBSYSTEM
 P:	Johannes Erdfelt
-M:	jerdfelt@valinux.com
 M:	johannes@erdfelt.com
 L:	linux-usb-users@lists.sourceforge.net
 L:	linux-usb-devel@lists.sourceforge.net
diff -urN linux-2.4.11-pre4/Makefile linux/Makefile
--- linux-2.4.11-pre4/Makefile	Mon Dec  3 23:16:27 2001
+++ linux/Makefile	Mon Dec  3 23:16:37 2001
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 4
 SUBLEVEL = 11
-EXTRAVERSION =-pre4
+EXTRAVERSION =-pre5
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
diff -urN linux-2.4.11-pre4/README linux/README
--- linux-2.4.11-pre4/README	Sun Mar 25 18:24:31 2001
+++ linux/README	Mon Dec  3 23:16:37 2001
@@ -192,11 +192,7 @@
    to the place where your regular bootable kernel is found. 
 
    For some, this is on a floppy disk, in which case you can copy the
-   kernel bzImage file to /dev/fd0 to make a bootable floppy. Please note
-   that you can not boot a kernel by directly dumping it to a 720k
-   double-density 3.5" floppy.  In this case, it is highly recommended
-   that you install LILO on your double-density boot floppy or switch to
-   high-density floppies.
+   kernel bzImage file to /dev/fd0 to make a bootable floppy.
 
    If you boot Linux from the hard drive, chances are you use LILO which
    uses the kernel image as specified in the file /etc/lilo.conf.  The
diff -urN linux-2.4.11-pre4/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S
--- linux-2.4.11-pre4/arch/i386/kernel/entry.S	Mon Dec  3 23:16:27 2001
+++ linux/arch/i386/kernel/entry.S	Mon Dec  3 23:16:37 2001
@@ -44,7 +44,6 @@
 #include <linux/sys.h>
 #include <linux/linkage.h>
 #include <asm/segment.h>
-#define ASSEMBLY
 #include <asm/smp.h>
 
 EBX		= 0x00
diff -urN linux-2.4.11-pre4/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c
--- linux-2.4.11-pre4/arch/i386/kernel/setup.c	Mon Dec  3 23:16:27 2001
+++ linux/arch/i386/kernel/setup.c	Mon Dec  3 23:16:37 2001
@@ -2416,17 +2416,16 @@
 	struct cpuinfo_x86 *c = cpu_data;
 	int i, n;
 
-	/* 
-	 * WARNING - nasty evil hack ... if we print > 8, it overflows the
-	 * page buffer and corrupts memory - this needs fixing properly
-	 */
-	for (n = 0; n < 8; n++, c++) {
-	/* for (n = 0; n < NR_CPUS; n++, c++) { */
+	for (n = 0; n < NR_CPUS; n++, c++) {
 		int fpu_exception;
 #ifdef CONFIG_SMP
 		if (!(cpu_online_map & (1<<n)))
 			continue;
 #endif
+		/* Stupid hack */
+		if (p - buffer > (3*PAGE_SIZE)/4)
+			break;
+
 		p += sprintf(p,"processor\t: %d\n"
 			"vendor_id\t: %s\n"
 			"cpu family\t: %d\n"
diff -urN linux-2.4.11-pre4/arch/mips/au1000/common/serial.c linux/arch/mips/au1000/common/serial.c
--- linux-2.4.11-pre4/arch/mips/au1000/common/serial.c	Sun Sep  9 10:43:01 2001
+++ linux/arch/mips/au1000/common/serial.c	Mon Dec  3 23:16:37 2001
@@ -68,9 +68,6 @@
  * End of serial driver configuration section.
  */
 
-#ifdef MODVERSIONS
-#include <linux/modversions.h>
-#endif
 #include <linux/module.h>
 
 #include <linux/types.h>
diff -urN linux-2.4.11-pre4/arch/mips/dec/wbflush.c linux/arch/mips/dec/wbflush.c
--- linux-2.4.11-pre4/arch/mips/dec/wbflush.c	Thu Feb 24 22:52:30 2000
+++ linux/arch/mips/dec/wbflush.c	Mon Dec  3 23:16:37 2001
@@ -103,8 +103,6 @@
 {
 }
 
-#ifdef EXPORT_SYMTAB
 #include <linux/module.h>
 
 EXPORT_SYMBOL(__wbflush);
-#endif
diff -urN linux-2.4.11-pre4/arch/parisc/hpux/entry_hpux.S linux/arch/parisc/hpux/entry_hpux.S
--- linux-2.4.11-pre4/arch/parisc/hpux/entry_hpux.S	Tue Dec  5 12:29:39 2000
+++ linux/arch/parisc/hpux/entry_hpux.S	Mon Dec  3 23:16:37 2001
@@ -6,8 +6,6 @@
  */
 
 
-#define ASSEMBLY
-
 #include <linux/sys.h>
 #include <linux/linkage.h>
 #include <asm/unistd.h>
diff -urN linux-2.4.11-pre4/arch/s390/kernel/entry.S linux/arch/s390/kernel/entry.S
--- linux-2.4.11-pre4/arch/s390/kernel/entry.S	Sun Aug 12 10:38:47 2001
+++ linux/arch/s390/kernel/entry.S	Mon Dec  3 23:16:37 2001
@@ -9,8 +9,6 @@
  *               Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com),
  */
 
-#define ASSEMBLY
-
 #include <linux/sys.h>
 #include <linux/linkage.h>
 #include <linux/config.h>
diff -urN linux-2.4.11-pre4/arch/s390x/kernel/entry.S linux/arch/s390x/kernel/entry.S
--- linux-2.4.11-pre4/arch/s390x/kernel/entry.S	Sun Aug 12 10:38:48 2001
+++ linux/arch/s390x/kernel/entry.S	Mon Dec  3 23:16:37 2001
@@ -9,8 +9,6 @@
  *               Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com),
  */
 
-#define ASSEMBLY
-
 #include <linux/sys.h>
 #include <linux/linkage.h>
 #include <linux/config.h>
diff -urN linux-2.4.11-pre4/arch/sparc64/kernel/head.S linux/arch/sparc64/kernel/head.S
--- linux-2.4.11-pre4/arch/sparc64/kernel/head.S	Thu Sep 20 14:11:57 2001
+++ linux/arch/sparc64/kernel/head.S	Mon Dec  3 23:16:37 2001
@@ -1,4 +1,4 @@
-/* $Id: head.S,v 1.81 2001/09/07 23:00:15 kanoj Exp $
+/* $Id: head.S,v 1.82 2001/10/04 23:37:04 davem Exp $
  * head.S: Initial boot code for the Sparc64 port of Linux.
  *
  * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu)
@@ -590,7 +590,8 @@
 
 	ldxa		[%g0] ASI_SAFARI_CONFIG, %g1
 	srlx		%g1, 17, %g1
-	and		%g1, 0x3ff, %g1		! 10bit Safari Agent ID
+	ba,pt		%xcc, set_worklist
+	 and		%g1, 0x3ff, %g1		! 10bit Safari Agent ID
 
 not_cheetah:
 	ldxa	[%g0] ASI_UPA_CONFIG, %g1
diff -urN linux-2.4.11-pre4/drivers/block/paride/Makefile linux/drivers/block/paride/Makefile
--- linux-2.4.11-pre4/drivers/block/paride/Makefile	Fri Apr  6 10:42:55 2001
+++ linux/drivers/block/paride/Makefile	Mon Dec  3 23:16:37 2001
@@ -7,6 +7,8 @@
 
 L_TARGET := paride.a
 
+export-objs := bpck6.o
+
 obj-$(CONFIG_PARIDE)		+= paride.o
 obj-$(CONFIG_PARIDE_PD)		+= pd.o
 obj-$(CONFIG_PARIDE_PCD)	+= pcd.o
diff -urN linux-2.4.11-pre4/drivers/block/paride/bpck6.c linux/drivers/block/paride/bpck6.c
--- linux-2.4.11-pre4/drivers/block/paride/bpck6.c	Mon Sep 17 22:52:34 2001
+++ linux/drivers/block/paride/bpck6.c	Mon Dec  3 23:16:37 2001
@@ -25,7 +25,6 @@
 
 #define BACKPACK_VERSION "2.0.2"
 
-#define EXPORT_SYMTAB 
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
diff -urN linux-2.4.11-pre4/drivers/char/Config.in linux/drivers/char/Config.in
--- linux-2.4.11-pre4/drivers/char/Config.in	Sun Sep 23 09:52:38 2001
+++ linux/drivers/char/Config.in	Mon Dec  3 23:16:37 2001
@@ -205,8 +205,8 @@
 
 dep_tristate '/dev/agpgart (AGP Support)' CONFIG_AGP $CONFIG_DRM_AGP
 if [ "$CONFIG_AGP" != "n" ]; then
-   bool '  Intel 440LX/BX/GX and I815/I840/I850 support' CONFIG_AGP_INTEL
-   bool '  Intel I810/I815 (on-board) support' CONFIG_AGP_I810
+   bool '  Intel 440LX/BX/GX and I815/I830M/I840/I850 support' CONFIG_AGP_INTEL
+   bool '  Intel I810/I815/I830M (on-board) support' CONFIG_AGP_I810
    bool '  VIA chipset support' CONFIG_AGP_VIA
    bool '  AMD Irongate, 761, and 762 support' CONFIG_AGP_AMD
    bool '  Generic SiS support' CONFIG_AGP_SIS
diff -urN linux-2.4.11-pre4/drivers/char/agp/agp.h linux/drivers/char/agp/agp.h
--- linux-2.4.11-pre4/drivers/char/agp/agp.h	Sun Sep 23 09:52:38 2001
+++ linux/drivers/char/agp/agp.h	Mon Dec  3 23:16:37 2001
@@ -133,6 +133,10 @@
 #define INREG16(mmap, addr)         __raw_readw((mmap)+(addr))
 #define INREG8(mmap, addr)         __raw_readb((mmap)+(addr))
 
+#define KB(x) ((x) * 1024)
+#define MB(x) (KB (KB (x)))
+#define GB(x) (MB (KB (x)))
+
 #define CACHE_FLUSH	agp_bridge.cache_flush
 #define A_SIZE_8(x)	((aper_size_info_8 *) x)
 #define A_SIZE_16(x)	((aper_size_info_16 *) x)
@@ -196,6 +200,12 @@
 #ifndef PCI_DEVICE_ID_INTEL_815_1
 #define PCI_DEVICE_ID_INTEL_815_1       0x1132
 #endif
+#ifndef PCI_DEVICE_ID_INTEL_830_M_0
+#define PCI_DEVICE_ID_INTEL_830_M_0     0x3575
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_830_M_1
+#define PCI_DEVICE_ID_INTEL_830_M_1     0x3577
+#endif
 #ifndef PCI_DEVICE_ID_INTEL_82443GX_1
 #define PCI_DEVICE_ID_INTEL_82443GX_1   0x71a1
 #endif
@@ -266,6 +276,22 @@
 #define I810_DRAM_CTL          0x3000
 #define I810_DRAM_ROW_0        0x00000001
 #define I810_DRAM_ROW_0_SDRAM  0x00000001
+
+/* intel i830 registers */
+#define I830_GMCH_CTRL             0x52
+#define I830_GMCH_ENABLED          0x4
+#define I830_GMCH_MEM_MASK         0x1
+#define I830_GMCH_MEM_64M          0x1
+#define I830_GMCH_MEM_128M         0
+#define I830_GMCH_GMS_MASK         0x70
+#define I830_GMCH_GMS_DISABLED     0x00
+#define I830_GMCH_GMS_LOCAL        0x10
+#define I830_GMCH_GMS_STOLEN_512   0x20
+#define I830_GMCH_GMS_STOLEN_1024  0x30
+#define I830_GMCH_GMS_STOLEN_8192  0x40
+#define I830_RDRAM_CHANNEL_TYPE    0x03010
+#define I830_RDRAM_ND(x)           (((x) & 0x20) >> 5)
+#define I830_RDRAM_DDT(x)          (((x) & 0x18) >> 3)
 
 /* VIA register */
 #define VIA_APBASE      0x10
diff -urN linux-2.4.11-pre4/drivers/char/agp/agpgart_be.c linux/drivers/char/agp/agpgart_be.c
--- linux-2.4.11-pre4/drivers/char/agp/agpgart_be.c	Sun Sep 23 09:52:38 2001
+++ linux/drivers/char/agp/agpgart_be.c	Mon Dec  3 23:16:37 2001
@@ -1113,6 +1113,290 @@
 	return 0;
 }
 
+static aper_size_info_fixed intel_i830_sizes[] =
+{
+	{128, 32768, 5},
+	/* The 64M mode still requires a 128k gatt */
+	{64, 16384, 5}
+};
+
+static struct _intel_i830_private {
+	struct pci_dev *i830_dev;   /* device one */
+	volatile u8 *registers;
+	int gtt_entries;
+} intel_i830_private;
+
+static void intel_i830_init_gtt_entries(void) {
+	u16 gmch_ctrl;
+	int gtt_entries;
+	u8 rdct;
+	static const int ddt[4] = { 0, 16, 32, 64 };
+
+	pci_read_config_word(agp_bridge.dev,I830_GMCH_CTRL,&gmch_ctrl);
+
+	switch (gmch_ctrl & I830_GMCH_GMS_MASK) {
+	case I830_GMCH_GMS_STOLEN_512:
+		gtt_entries = KB(512);
+		printk(KERN_INFO PFX "detected %dK stolen memory.\n",gtt_entries / KB(1));
+		break;
+	case I830_GMCH_GMS_STOLEN_1024:
+		gtt_entries = MB(1);
+		printk(KERN_INFO PFX "detected %dK stolen memory.\n",gtt_entries / KB(1));
+		break;
+	case I830_GMCH_GMS_STOLEN_8192:
+		gtt_entries = MB(8);
+		printk(KERN_INFO PFX "detected %dK stolen memory.\n",gtt_entries / KB(1));
+		break;
+	case I830_GMCH_GMS_LOCAL:
+		rdct = INREG8(intel_i830_private.registers,I830_RDRAM_CHANNEL_TYPE);
+		gtt_entries = (I830_RDRAM_ND(rdct) + 1) * MB(ddt[I830_RDRAM_DDT(rdct)]);
+		printk(KERN_INFO PFX "detected %dK local memory.\n",gtt_entries / KB(1));
+		break;
+	default:
+		printk(KERN_INFO PFX "no video memory detected.\n");
+		gtt_entries = 0;
+		break;
+	}
+
+	gtt_entries /= KB(4);
+
+	intel_i830_private.gtt_entries = gtt_entries;
+}
+
+/* The intel i830 automatically initializes the agp aperture during POST.
+ * Use the memory already set aside for in the GTT.
+ */
+static int intel_i830_create_gatt_table(void)
+{
+	int page_order;
+	aper_size_info_fixed *size;
+	int num_entries;
+	u32 temp;
+
+	size = agp_bridge.current_size;
+	page_order = size->page_order;
+	num_entries = size->num_entries;
+	agp_bridge.gatt_table_real = 0;
+
+	pci_read_config_dword(intel_i830_private.i830_dev,I810_MMADDR,&temp);
+	temp &= 0xfff80000;
+
+	intel_i830_private.registers = (volatile u8 *) ioremap(temp,128 * 4096);
+	if (!intel_i830_private.registers) return (-ENOMEM);
+
+	temp = INREG32(intel_i830_private.registers,I810_PGETBL_CTL) & 0xfffff000;
+	CACHE_FLUSH();
+
+	/* we have to call this as early as possible after the MMIO base address is known */
+	intel_i830_init_gtt_entries();
+
+	agp_bridge.gatt_table = NULL;
+
+	agp_bridge.gatt_bus_addr = temp;
+
+	return(0);
+}
+
+/* Return the gatt table to a sane state. Use the top of stolen
+ * memory for the GTT.
+ */
+static int intel_i830_free_gatt_table(void)
+{
+	return(0);
+}
+
+static int intel_i830_fetch_size(void)
+{
+	u16 gmch_ctrl;
+	aper_size_info_fixed *values;
+
+	pci_read_config_word(agp_bridge.dev,I830_GMCH_CTRL,&gmch_ctrl);
+	values = A_SIZE_FIX(agp_bridge.aperture_sizes);
+
+	if ((gmch_ctrl & I830_GMCH_MEM_MASK) == I830_GMCH_MEM_128M) {
+		agp_bridge.previous_size = agp_bridge.current_size = (void *) values;
+		agp_bridge.aperture_size_idx = 0;
+		return(values[0].size);
+	} else {
+		agp_bridge.previous_size = agp_bridge.current_size = (void *) values;
+		agp_bridge.aperture_size_idx = 1;
+		return(values[1].size);
+	}
+
+	return(0);
+}
+
+static int intel_i830_configure(void)
+{
+	aper_size_info_fixed *current_size;
+	u32 temp;
+	u16 gmch_ctrl;
+	int i;
+
+	current_size = A_SIZE_FIX(agp_bridge.current_size);
+
+	pci_read_config_dword(intel_i830_private.i830_dev,I810_GMADDR,&temp);
+	agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+
+	pci_read_config_word(agp_bridge.dev,I830_GMCH_CTRL,&gmch_ctrl);
+	gmch_ctrl |= I830_GMCH_ENABLED;
+	pci_write_config_word(agp_bridge.dev,I830_GMCH_CTRL,gmch_ctrl);
+
+	OUTREG32(intel_i830_private.registers,I810_PGETBL_CTL,agp_bridge.gatt_bus_addr | I810_PGETBL_ENABLED);
+	CACHE_FLUSH();
+
+	if (agp_bridge.needs_scratch_page == TRUE)
+		for (i = intel_i830_private.gtt_entries; i < current_size->num_entries; i++)
+			OUTREG32(intel_i830_private.registers,I810_PTE_BASE + (i * 4),agp_bridge.scratch_page);
+
+	return (0);
+}
+
+static void intel_i830_cleanup(void)
+{
+	iounmap((void *) intel_i830_private.registers);
+}
+
+static int intel_i830_insert_entries(agp_memory *mem,off_t pg_start,int type)
+{
+	int i,j,num_entries;
+	void *temp;
+
+	temp = agp_bridge.current_size;
+	num_entries = A_SIZE_FIX(temp)->num_entries;
+
+	if (pg_start < intel_i830_private.gtt_entries) {
+		printk (KERN_DEBUG "pg_start == 0x%.8lx,intel_i830_private.gtt_entries == 0x%.8x\n",
+				pg_start,intel_i830_private.gtt_entries);
+
+		printk ("Trying to insert into local/stolen memory\n");
+		return (-EINVAL);
+	}
+
+	if ((pg_start + mem->page_count) > num_entries)
+		return (-EINVAL);
+
+	/* The i830 can't check the GTT for entries since its read only,
+	 * depend on the caller to make the correct offset decisions.
+	 */
+
+	if ((type != 0 && type != AGP_PHYS_MEMORY) ||
+		(mem->type != 0 && mem->type != AGP_PHYS_MEMORY))
+		return (-EINVAL);
+
+	CACHE_FLUSH();
+
+	for (i = 0, j = pg_start; i < mem->page_count; i++, j++)
+		OUTREG32(intel_i830_private.registers,I810_PTE_BASE + (j * 4),mem->memory[i]);
+
+	CACHE_FLUSH();
+
+	agp_bridge.tlb_flush(mem);
+
+	return(0);
+}
+
+static int intel_i830_remove_entries(agp_memory *mem,off_t pg_start,int type)
+{
+	int i;
+
+	CACHE_FLUSH ();
+
+	if (pg_start < intel_i830_private.gtt_entries) {
+		printk ("Trying to disable local/stolen memory\n");
+		return (-EINVAL);
+	}
+
+	for (i = pg_start; i < (mem->page_count + pg_start); i++)
+		OUTREG32(intel_i830_private.registers,I810_PTE_BASE + (i * 4),agp_bridge.scratch_page);
+
+	CACHE_FLUSH();
+
+	agp_bridge.tlb_flush(mem);
+
+	return (0);
+}
+
+static agp_memory *intel_i830_alloc_by_type(size_t pg_count,int type)
+{
+	agp_memory *nw;
+
+	/* always return NULL for now */
+	if (type == AGP_DCACHE_MEMORY) return(NULL);
+
+	if (type == AGP_PHYS_MEMORY) {
+		unsigned long physical;
+
+		/* The i830 requires a physical address to program
+		 * it's mouse pointer into hardware. However the
+		 * Xserver still writes to it through the agp
+		 * aperture
+		 */
+
+		if (pg_count != 1) return(NULL);
+
+		nw = agp_create_memory(1);
+
+		if (nw == NULL) return(NULL);
+
+		MOD_INC_USE_COUNT;
+		nw->memory[0] = agp_bridge.agp_alloc_page();
+		physical = nw->memory[0];
+		if (nw->memory[0] == 0) {
+			/* free this structure */
+			agp_free_memory(nw);
+			return(NULL);
+		}
+
+		nw->memory[0] = agp_bridge.mask_memory(virt_to_phys((void *) nw->memory[0]),type);
+		nw->page_count = 1;
+		nw->num_scratch_pages = 1;
+		nw->type = AGP_PHYS_MEMORY;
+		nw->physical = virt_to_phys((void *) physical);
+		return(nw);
+	}
+
+	return(NULL);
+}
+
+static int __init intel_i830_setup(struct pci_dev *i830_dev)
+{
+	intel_i830_private.i830_dev = i830_dev;
+
+	agp_bridge.masks = intel_i810_masks;
+	agp_bridge.num_of_masks = 3;
+	agp_bridge.aperture_sizes = (void *) intel_i830_sizes;
+	agp_bridge.size_type = FIXED_APER_SIZE;
+	agp_bridge.num_aperture_sizes = 2;
+
+	agp_bridge.dev_private_data = (void *) &intel_i830_private;
+	agp_bridge.needs_scratch_page = TRUE;
+
+	agp_bridge.configure = intel_i830_configure;
+	agp_bridge.fetch_size = intel_i830_fetch_size;
+	agp_bridge.cleanup = intel_i830_cleanup;
+	agp_bridge.tlb_flush = intel_i810_tlbflush;
+	agp_bridge.mask_memory = intel_i810_mask_memory;
+	agp_bridge.agp_enable = intel_i810_agp_enable;
+	agp_bridge.cache_flush = global_cache_flush;
+
+	agp_bridge.create_gatt_table = intel_i830_create_gatt_table;
+	agp_bridge.free_gatt_table = intel_i830_free_gatt_table;
+
+	agp_bridge.insert_memory = intel_i830_insert_entries;
+	agp_bridge.remove_memory = intel_i830_remove_entries;
+	agp_bridge.alloc_by_type = intel_i830_alloc_by_type;
+	agp_bridge.free_by_type = intel_i810_free_by_type;
+	agp_bridge.agp_alloc_page = agp_generic_alloc_page;
+	agp_bridge.agp_destroy_page = agp_generic_destroy_page;
+
+	agp_bridge.suspend = agp_generic_suspend;
+	agp_bridge.resume = agp_generic_resume;
+	agp_bridge.cant_use_aperture = 0;
+
+	return(0);
+}
+
 #endif /* CONFIG_AGP_I810 */
 
 #ifdef CONFIG_AGP_INTEL
@@ -2976,6 +3260,12 @@
 		"Intel",
 		"i815",
 		intel_generic_setup },
+	{ PCI_DEVICE_ID_INTEL_830_M_0,
+		PCI_VENDOR_ID_INTEL,
+		INTEL_I830_M,
+		"Intel",
+		"i830M",
+		intel_generic_setup },
 	{ PCI_DEVICE_ID_INTEL_840_0,
 		PCI_VENDOR_ID_INTEL,
 		INTEL_I840,
@@ -3240,17 +3530,38 @@
 						   PCI_DEVICE_ID_INTEL_815_1,
 						   NULL);
 			if (i810_dev == NULL) {
-				printk(KERN_ERR PFX "agpgart: Detected an "
+				printk(KERN_ERR PFX "Detected an "
 				       "Intel i815, but could not find the"
 				       " secondary device. Assuming a "
 				       "non-integrated video card.\n");
 				break;
 			}
-			printk(KERN_INFO PFX "agpgart: Detected an Intel i815 "
+			printk(KERN_INFO PFX "Detected an Intel i815 "
 			       "Chipset.\n");
 			agp_bridge.type = INTEL_I810;
 			return intel_i810_setup(i810_dev);
 
+		case PCI_DEVICE_ID_INTEL_830_M_0:
+			i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL,
+									   PCI_DEVICE_ID_INTEL_830_M_1,
+									   NULL);
+			if(PCI_FUNC(i810_dev->devfn) != 0) {
+				i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL,
+										   PCI_DEVICE_ID_INTEL_830_M_1,
+										   i810_dev);
+			}
+
+			if (i810_dev == NULL) {
+				printk(KERN_ERR PFX "Detected an "
+					   "Intel 830M, but could not find the"
+					   " secondary device.\n");
+				agp_bridge.type = NOT_SUPPORTED;
+				return -ENODEV;
+			}
+			printk(KERN_INFO PFX "Detected an Intel "
+				   "830M Chipset.\n");
+			agp_bridge.type = INTEL_I810;
+			return intel_i830_setup(i810_dev);
 		default:
 			break;
 		}
diff -urN linux-2.4.11-pre4/drivers/media/video/cpia.c linux/drivers/media/video/cpia.c
--- linux-2.4.11-pre4/drivers/media/video/cpia.c	Sat May 19 17:43:06 2001
+++ linux/drivers/media/video/cpia.c	Mon Dec  3 23:16:37 2001
@@ -3,9 +3,9 @@
  *
  * Supports CPiA based Video Camera's.
  *
- * (C) Copyright 1999-2000 Peter Pregler,
- * (C) Copyright 1999-2000 Scott J. Bertin,
- * (C) Copyright 1999-2000 Johannes Erdfelt, jerdfelt@valinux.com
+ * (C) Copyright 1999-2000 Peter Pregler
+ * (C) Copyright 1999-2000 Scott J. Bertin
+ * (C) Copyright 1999-2000 Johannes Erdfelt <johannes@erdfelt.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -56,7 +56,7 @@
 
 #ifdef MODULE
 MODULE_PARM(video_nr,"i");
-MODULE_AUTHOR("Scott J. Bertin <sbertin@mindspring.com> & Peter Pregler <Peter_Pregler@email.com> & Johannes Erdfelt <jerdfelt@valinux.com>");
+MODULE_AUTHOR("Scott J. Bertin <sbertin@mindspring.com> & Peter Pregler <Peter_Pregler@email.com> & Johannes Erdfelt <johannes@erdfelt.com>");
 MODULE_DESCRIPTION("V4L-driver for Vision CPiA based cameras");
 MODULE_SUPPORTED_DEVICE("video");
 #endif
diff -urN linux-2.4.11-pre4/drivers/media/video/cpia_usb.c linux/drivers/media/video/cpia_usb.c
--- linux-2.4.11-pre4/drivers/media/video/cpia_usb.c	Mon Feb 19 10:18:18 2001
+++ linux/drivers/media/video/cpia_usb.c	Mon Dec  3 23:16:37 2001
@@ -4,7 +4,7 @@
  * Supports CPiA based parallel port Video Camera's.
  *
  * Copyright (C) 1999        Jochen Scharrlach <Jochen.Scharrlach@schwaben.de>
- * Copyright (C) 1999, 2000  Johannes Erdfelt <jerdfelt@valinux.com>
+ * Copyright (C) 1999, 2000  Johannes Erdfelt <johannes@erdfelt.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
diff -urN linux-2.4.11-pre4/drivers/mtd/Config.in linux/drivers/mtd/Config.in
--- linux-2.4.11-pre4/drivers/mtd/Config.in	Mon Dec  3 23:16:27 2001
+++ linux/drivers/mtd/Config.in	Mon Dec  3 23:16:37 2001
@@ -1,5 +1,5 @@
 
-# $Id: Config.in,v 1.70 2001/08/11 16:13:38 dwmw2 Exp $
+# $Id: Config.in,v 1.71 2001/10/03 11:38:38 dwmw2 Exp $
 
 mainmenu_option next_comment
 comment 'Memory Technology Devices (MTD)'
@@ -13,8 +13,10 @@
    fi
    dep_tristate '  MTD partitioning support' CONFIG_MTD_PARTITIONS $CONFIG_MTD
    dep_tristate '  RedBoot partition table parsing' CONFIG_MTD_REDBOOT_PARTS $CONFIG_MTD_PARTITIONS
+if [ "$CONFIG_ARM" = "y" ]; then
    dep_tristate '  Compaq bootldr partition table parsing' CONFIG_MTD_BOOTLDR_PARTS $CONFIG_MTD_PARTITIONS
    dep_tristate '  ARM Firmware Suite partition parsing' CONFIG_MTD_AFS_PARTS $CONFIG_MTD_PARTITIONS
+fi
 
 comment 'User Modules And Translation Layers'
    dep_tristate '  Direct char device access to MTD devices' CONFIG_MTD_CHAR $CONFIG_MTD
diff -urN linux-2.4.11-pre4/drivers/mtd/chips/Makefile linux/drivers/mtd/chips/Makefile
--- linux-2.4.11-pre4/drivers/mtd/chips/Makefile	Mon Dec  3 23:16:27 2001
+++ linux/drivers/mtd/chips/Makefile	Mon Dec  3 23:16:37 2001
@@ -1,11 +1,11 @@
 #
 # linux/drivers/chips/Makefile
 #
-# $Id: Makefile,v 1.6 2001/09/02 18:57:01 dwmw2 Exp $
+# $Id: Makefile,v 1.7 2001/10/05 06:53:51 dwmw2 Exp $
 
 O_TARGET	:= chipslink.o
 
-export-objs	:= chipreg.o
+export-objs	:= chipreg.o gen_probe.o
 
 #                       *** BIG UGLY NOTE ***
 #
diff -urN linux-2.4.11-pre4/drivers/mtd/devices/blkmtd.c linux/drivers/mtd/devices/blkmtd.c
--- linux-2.4.11-pre4/drivers/mtd/devices/blkmtd.c	Mon Dec  3 23:16:27 2001
+++ linux/drivers/mtd/devices/blkmtd.c	Mon Dec  3 23:16:37 2001
@@ -59,11 +59,6 @@
 #include <linux/mtd/compatmac.h>
 #include <linux/mtd/mtd.h>
 
-#if CONFIG_MODVERSION==1
-#define MODVERSIONS
-#include <linux/modversions.h>
-#endif
-
 /* Default erase size in K, always make it a multiple of PAGE_SIZE */
 #define CONFIG_MTD_BLKDEV_ERASESIZE 128
 #define VERSION "1.1"
diff -urN linux-2.4.11-pre4/drivers/scsi/aic7xxx/aic7xxx_linux.c linux/drivers/scsi/aic7xxx/aic7xxx_linux.c
--- linux-2.4.11-pre4/drivers/scsi/aic7xxx/aic7xxx_linux.c	Mon Dec  3 23:16:28 2001
+++ linux/drivers/scsi/aic7xxx/aic7xxx_linux.c	Mon Dec  3 23:16:38 2001
@@ -1175,6 +1175,7 @@
 	host->irq = ahc->platform_data->irq;
 	host->max_id = (ahc->features & AHC_WIDE) ? 16 : 8;
 	host->max_channel = (ahc->features & AHC_TWIN) ? 1 : 0;
+	host->max_lun = AHC_NUM_LUNS;
 	ahc_set_unit(ahc, ahc_linux_next_unit());
 	sprintf(buf, "scsi%d", host->host_no);
 	new_name = malloc(strlen(buf) + 1, M_DEVBUF, M_NOWAIT);
diff -urN linux-2.4.11-pre4/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- linux-2.4.11-pre4/drivers/scsi/scsi.c	Mon Dec  3 23:16:28 2001
+++ linux/drivers/scsi/scsi.c	Mon Dec  3 23:16:38 2001
@@ -629,6 +629,7 @@
 	if (++serial_number == 0)
 		serial_number = 1;
 	SCpnt->serial_number = serial_number;
+	SCpnt->pid = scsi_pid++;
 
 	/*
 	 * We will wait MIN_RESET_DELAY clock ticks after the last reset so
@@ -968,6 +969,7 @@
 
 	ASSERT_LOCK(&io_request_lock, 0);
 
+	SCpnt->pid = scsi_pid++;
 	SCpnt->owner = SCSI_OWNER_MIDLEVEL;
 
 	SCSI_LOG_MLQUEUE(4,
diff -urN linux-2.4.11-pre4/drivers/scsi/scsi_scan.c linux/drivers/scsi/scsi_scan.c
--- linux-2.4.11-pre4/drivers/scsi/scsi_scan.c	Mon Aug 13 16:36:30 2001
+++ linux/drivers/scsi/scsi_scan.c	Mon Dec  3 23:16:38 2001
@@ -795,19 +795,7 @@
 	 * other settings, and scan all of them.
 	 */
 	if (bflags & BLIST_SPARSELUN) {
-		/*
-		 * Scanning MAX_SCSI_LUNS units would be a bad idea.
-		 * Any better idea?
-		 * I think we need REPORT LUNS in future to avoid scanning
-		 * of unused LUNs. But, that is another item.
-		 *
-		 * FIXME(eric) - perhaps this should be a kernel configurable?
-		 */
-		if (*max_dev_lun < shpnt->max_lun)
-			*max_dev_lun = shpnt->max_lun;
-		else 	if ((max_scsi_luns >> 1) >= *max_dev_lun)
-				*max_dev_lun += shpnt->max_lun;
-			else	*max_dev_lun = max_scsi_luns;
+		*max_dev_lun = shpnt->max_lun;
 		*sparse_lun = 1;
 		return 1;
 	}
diff -urN linux-2.4.11-pre4/drivers/usb/CDCEther.c linux/drivers/usb/CDCEther.c
--- linux-2.4.11-pre4/drivers/usb/CDCEther.c	Sun Sep 23 09:49:00 2001
+++ linux/drivers/usb/CDCEther.c	Mon Dec  3 23:16:38 2001
@@ -31,12 +31,10 @@
 
 static const char *version = __FILE__ ": v0.98.5 22 Sep 2001 Brad Hards and another";
 
-/* We need to be selective about what we try to match on, to avoiding loading for a CDC
- * ACM (ISDN or PSTN) modem */
+/* Take any CDC device, and sort it out in probe() */
 static struct usb_device_id CDCEther_ids[] = {
-        { match_flags: (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS),
-          bInterfaceClass: USB_CLASS_COMM, bInterfaceSubClass: 6},
-        { } /* Terminating null entry */
+	{ USB_DEVICE_INFO(USB_CLASS_COMM, 0, 0) },
+	{ } /* Terminating null entry */
 };
 
 /* 
diff -urN linux-2.4.11-pre4/drivers/usb/Config.in linux/drivers/usb/Config.in
--- linux-2.4.11-pre4/drivers/usb/Config.in	Sun Sep 23 09:49:00 2001
+++ linux/drivers/usb/Config.in	Mon Dec  3 23:16:38 2001
@@ -59,6 +59,7 @@
    dep_tristate '  USB Mustek MDC800 Digital Camera support (EXPERIMENTAL)' CONFIG_USB_MDC800 $CONFIG_USB $CONFIG_EXPERIMENTAL
    dep_tristate '  USB Scanner support' CONFIG_USB_SCANNER $CONFIG_USB
    dep_tristate '  Microtek X6USB scanner support' CONFIG_USB_MICROTEK $CONFIG_USB $CONFIG_SCSI
+   dep_tristate '  HP53xx USB scanner support (EXPERIMENTAL)' CONFIG_USB_HPUSBSCSI $CONFIG_USB $CONFIG_SCSI $CONFIG_EXPERIMENTAL
 
    comment 'USB Multimedia devices'
    dep_tristate '  USB IBM (Xirlink) C-it Camera support' CONFIG_USB_IBMCAM $CONFIG_USB $CONFIG_VIDEO_DEV
diff -urN linux-2.4.11-pre4/drivers/usb/Makefile linux/drivers/usb/Makefile
--- linux-2.4.11-pre4/drivers/usb/Makefile	Sun Sep 23 09:49:00 2001
+++ linux/drivers/usb/Makefile	Mon Dec  3 23:16:38 2001
@@ -59,7 +59,7 @@
 obj-$(CONFIG_USB_ACM)		+= acm.o
 obj-$(CONFIG_USB_PRINTER)	+= printer.o
 obj-$(CONFIG_USB_AUDIO)		+= audio.o
-obj-$(CONFIG_USB_IBMCAM)	+= ibmcam.o
+obj-$(CONFIG_USB_IBMCAM)	+= ibmcam.o usbvideo.o ultracam.o
 obj-$(CONFIG_USB_PWC)		+= pwc.o
 obj-$(CONFIG_USB_DC2XX)		+= dc2xx.o
 obj-$(CONFIG_USB_MDC800)	+= mdc800.o
@@ -74,6 +74,7 @@
 obj-$(CONFIG_USB_RIO500)	+= rio500.o
 obj-$(CONFIG_USB_DSBR)		+= dsbr100.o
 obj-$(CONFIG_USB_MICROTEK)	+= microtek.o
+obj-$(CONFIG_USB_HPUSBSCSI)	+= hpusbscsi.o
 obj-$(CONFIG_USB_BLUETOOTH)	+= bluetooth.o
 obj-$(CONFIG_USB_USBNET)	+= usbnet.o
 
diff -urN linux-2.4.11-pre4/drivers/usb/acm.c linux/drivers/usb/acm.c
--- linux-2.4.11-pre4/drivers/usb/acm.c	Sun Sep 23 09:49:00 2001
+++ linux/drivers/usb/acm.c	Mon Dec  3 23:16:38 2001
@@ -1,9 +1,9 @@
 /*
- * acm.c  Version 0.20
+ * acm.c  Version 0.21
  *
  * Copyright (c) 1999 Armin Fuerst	<fuerst@in.tum.de>
  * Copyright (c) 1999 Pavel Machek	<pavel@suse.cz>
- * Copyright (c) 1999 Johannes Erdfelt	<jerdfelt@valinux.com>
+ * Copyright (c) 1999 Johannes Erdfelt	<johannes@erdfelt.com>
  * Copyright (c) 2000 Vojtech Pavlik	<vojtech@suse.cz>
  *
  * USB Abstract Control Model driver for USB modems and ISDN adapters
@@ -22,7 +22,8 @@
  *	v0.17 - added new style probing
  *	v0.18 - fixed new style probing for devices with more configurations
  *	v0.19 - fixed CLOCAL handling (thanks to Richard Shih-Ping Chan)
- *      v0.20 - switched to probing on interface (rather than device) class
+ *	v0.20 - switched to probing on interface (rather than device) class
+ *	v0.21 - revert to probing on device for devices with multiple configs
  */
 
 /*
@@ -60,7 +61,7 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v0.20"
+#define DRIVER_VERSION "v0.21"
 #define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik"
 #define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
 
@@ -648,8 +649,7 @@
  */
 
 static struct usb_device_id acm_ids[] = {
-	{match_flags: (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS),
-	bInterfaceClass: USB_CLASS_COMM, bInterfaceSubClass: 2},
+	{ USB_DEVICE_INFO(USB_CLASS_COMM, 0, 0) },
 	{ }
 };
 
diff -urN linux-2.4.11-pre4/drivers/usb/audio.c linux/drivers/usb/audio.c
--- linux-2.4.11-pre4/drivers/usb/audio.c	Tue Jun 12 15:53:37 2001
+++ linux/drivers/usb/audio.c	Mon Dec  3 23:16:38 2001
@@ -3,7 +3,7 @@
 /*
  *	audio.c  --  USB Audio Class driver
  *
- *	Copyright (C) 1999, 2000
+ *	Copyright (C) 1999, 2000, 2001
  *	    Alan Cox (alan@lxorguk.ukuu.org.uk)
  *	    Thomas Sailer (sailer@ife.ee.ethz.ch)
  *
@@ -12,6 +12,8 @@
  *	the Free Software Foundation; either version 2 of the License, or
  *	(at your option) any later version.
  *
+ * Debugging:
+ * 	Use the 'lsusb' utility to dump the descriptors.
  *
  * 1999-09-07:  Alan Cox
  *		Parsing Audio descriptor patch
@@ -92,9 +94,11 @@
  * 2000-11-26:  Thomas Sailer
  *              Workaround for Dallas DS4201. The DS4201 uses PCM8 as format tag for
  *              its 8 bit modes, but expects signed data (and should therefore have used PCM).
- * 2001-04-08:  gb
- *              Identify version on module load.
- *
+ * 2001-03-10:  Thomas Sailer
+ *              provide abs function, prevent picking up a bogus kernel macro
+ *              for abs. Bug report by Andrew Morton <andrewm@uow.edu.au>
+ * 2001-06-16:  Bryce Nesbitt <bryce@obviously.com>
+ *              Fix SNDCTL_DSP_STEREO API violation
  */
 
 /*
@@ -205,6 +209,9 @@
 
 #define dprintk(x)
 
+#undef abs
+extern int abs(int __x) __attribute__ ((__const__)); /* Shut up warning */
+
 /* --------------------------------------------------------------------- */
 
 /*
@@ -390,6 +397,17 @@
 
 /* --------------------------------------------------------------------- */
 
+/* prevent picking up a bogus abs macro */
+#undef abs
+extern inline int abs(int x)
+{
+        if (x < 0)
+		return -x;
+	return x;
+}
+                                
+/* --------------------------------------------------------------------- */
+
 extern inline unsigned ld2(unsigned int x)
 {
 	unsigned r = 0;
@@ -485,10 +503,10 @@
 	}
 	db->bufsize = nr << PAGE_SHIFT;
 	db->ready = 1;
-	dprintk((KERN_DEBUG "dmabuf_init: bytepersec %d bufs %d ossfragshift %d ossmaxfrags %d "
-	         "fragshift %d fragsize %d numfrag %d dmasize %d bufsize %d fmt 0x%x\n",
+	dprintk((KERN_DEBUG "usbaudio: dmabuf_init bytepersec %d bufs %d ossfragshift %d ossmaxfrags %d "
+	         "fragshift %d fragsize %d numfrag %d dmasize %d bufsize %d fmt 0x%x srate %d\n",
 	         bytepersec, bufs, db->ossfragshift, db->ossmaxfrags, db->fragshift, db->fragsize,
-	         db->numfrag, db->dmasize, db->bufsize, db->format));
+	         db->numfrag, db->dmasize, db->bufsize, db->format, db->srate));
 	return 0;
 }
 
@@ -860,9 +878,11 @@
 		u->dma.count += cnt;
 		if (u->format == u->dma.format) {
 			/* we do not need format conversion */
+			dprintk((KERN_DEBUG "usbaudio: no sample format conversion\n"));
 			dmabuf_copyin(&u->dma, cp, cnt);
 		} else {
 			/* we need sampling format conversion */
+			dprintk((KERN_DEBUG "usbaudio: sample format conversion %x != %x\n", u->format, u->dma.format));
 			usbin_convert(u, cp, scnt);
 		}
 	}
@@ -1533,7 +1553,7 @@
 		d->srate = fmt->sratelo;
 	if (d->srate > fmt->sratehi)
 		d->srate = fmt->sratehi;
-	dprintk((KERN_DEBUG "usb_audio: set_format_in: usb_set_interface %u %u\n", alts->bInterfaceNumber, fmt->altsetting));
+	dprintk((KERN_DEBUG "usbaudio: set_format_in: usb_set_interface %u %u\n", alts->bInterfaceNumber, fmt->altsetting));
 	if (usb_set_interface(dev, alts->bInterfaceNumber, fmt->altsetting) < 0) {
 		printk(KERN_WARNING "usbaudio: usb_set_interface failed, device %d interface %d altsetting %d\n",
 		       dev->devnum, u->interface, fmt->altsetting);
@@ -1628,7 +1648,7 @@
 		d->srate = fmt->sratelo;
 	if (d->srate > fmt->sratehi)
 		d->srate = fmt->sratehi;
-	dprintk((KERN_DEBUG "usb_audio: set_format_out: usb_set_interface %u %u\n", alts->bInterfaceNumber, fmt->altsetting));
+	dprintk((KERN_DEBUG "usbaudio: set_format_out: usb_set_interface %u %u\n", alts->bInterfaceNumber, fmt->altsetting));
 	if (usb_set_interface(dev, u->interface, fmt->altsetting) < 0) {
 		printk(KERN_WARNING "usbaudio: usb_set_interface failed, device %d interface %d altsetting %d\n",
 		       dev->devnum, u->interface, fmt->altsetting);
@@ -1925,13 +1945,6 @@
 
 /* --------------------------------------------------------------------- */
 
-static loff_t usb_audio_llseek(struct file *file, loff_t offset, int origin)
-{
-	return -ESPIPE;
-}
-
-/* --------------------------------------------------------------------- */
-
 static int usb_audio_open_mixdev(struct inode *inode, struct file *file)
 {
 	int minor = MINOR(inode->i_rdev);
@@ -2071,7 +2084,7 @@
 
 static /*const*/ struct file_operations usb_mixer_fops = {
 	owner:		THIS_MODULE,
-	llseek:		usb_audio_llseek,
+	llseek:		no_llseek,
 	ioctl:		usb_audio_ioctl_mixdev,
 	open:		usb_audio_open_mixdev,
 	release:	usb_audio_release_mixdev,
@@ -2341,12 +2354,18 @@
 	unsigned long flags;
 	audio_buf_info abinfo;
 	count_info cinfo;
-	int val, val2, mapped, ret;
+	int val = 0;
+	int val2, mapped, ret;
 
 	if (!s->usbdev)
 		return -EIO;
 	mapped = ((file->f_mode & FMODE_WRITE) && as->usbout.dma.mapped) ||
 		((file->f_mode & FMODE_READ) && as->usbin.dma.mapped);
+#if 0
+	if (arg)
+		get_user(val, (int *)arg);
+	printk(KERN_DEBUG "usbaudio: usb_audio_ioctl cmd=%x arg=%lx *arg=%d\n", cmd, arg, val)
+#endif
 	switch (cmd) {
 	case OSS_GETVERSION:
 		return put_user(SOUND_VERSION, (int *)arg);
@@ -2388,8 +2407,14 @@
 		return put_user((file->f_mode & FMODE_READ) ? as->usbin.dma.srate : as->usbout.dma.srate, (int *)arg);
 
 	case SNDCTL_DSP_STEREO:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
 		val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format;
-		if (set_format(as, file->f_mode, val2 | AFMT_STEREO, 0))
+		if (val)
+			val2 |= AFMT_STEREO;
+		else
+			val2 &= ~AFMT_STEREO;
+		if (set_format(as, file->f_mode, val2, 0))
 			return -EIO;
 		return 0;
 
@@ -2590,6 +2615,7 @@
 	case SOUND_PCM_READ_FILTER:
 		return -EINVAL;
 	}
+	dprintk((KERN_DEBUG "usbaudio: usb_audio_ioctl - no command found\n"));
 	return -ENOIOCTLCMD;
 }
 
@@ -2690,7 +2716,7 @@
 
 static /*const*/ struct file_operations usb_audio_fops = {
 	owner:		THIS_MODULE,
-	llseek:		usb_audio_llseek,
+	llseek:		no_llseek,
 	read:		usb_audio_read,
 	write:		usb_audio_write,
 	poll:		usb_audio_poll,
@@ -2815,8 +2841,10 @@
 			if (alts->bInterfaceClass != USB_CLASS_AUDIO || alts->bInterfaceSubClass != 2)
 				continue;
 			if (alts->bNumEndpoints < 1) {
-				printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u does not have an endpoint\n", 
-				       dev->devnum, asifin, i);
+				if (i != 0) {  /* altsetting 0 has no endpoints (Section B.3.4.1) */
+					printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u does not have an endpoint\n", 
+					       dev->devnum, asifin, i);
+				}
 				continue;
 			}
 			if ((alts->endpoint[0].bmAttributes & 0x03) != 0x01 ||
@@ -2871,8 +2899,10 @@
 			fp->format = format;
 			fp->altsetting = i;
 			fp->sratelo = fp->sratehi = fmt[8] | (fmt[9] << 8) | (fmt[10] << 16);
+			printk(KERN_INFO "usbaudio: valid input sample rate %u\n", fp->sratelo);
 			for (j = fmt[7] ? (fmt[7]-1) : 1; j > 0; j--) {
 				k = fmt[8+3*j] | (fmt[9+3*j] << 8) | (fmt[10+3*j] << 16);
+				printk(KERN_INFO "usbaudio: valid input sample rate %u\n", k);
 				if (k > fp->sratehi)
 					fp->sratehi = k;
 				if (k < fp->sratelo)
@@ -2902,6 +2932,7 @@
 				       dev->devnum, asifout, i);
 				continue;
 			}
+			/* See USB audio formats manual, section 2 */
 			fmt = find_csinterface_descriptor(buffer, buflen, NULL, AS_GENERAL, asifout, i);
 			if (!fmt) {
 				printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not found\n", 
@@ -2951,8 +2982,10 @@
 			fp->format = format;
 			fp->altsetting = i;
 			fp->sratelo = fp->sratehi = fmt[8] | (fmt[9] << 8) | (fmt[10] << 16);
+			printk(KERN_INFO "usbaudio: valid output sample rate %u\n", fp->sratelo);
 			for (j = fmt[7] ? (fmt[7]-1) : 1; j > 0; j--) {
 				k = fmt[8+3*j] | (fmt[9+3*j] << 8) | (fmt[10+3*j] << 16);
+				printk(KERN_INFO "usbaudio: valid output sample rate %u\n", k);
 				if (k > fp->sratehi)
 					fp->sratehi = k;
 				if (k < fp->sratelo)
@@ -2972,6 +3005,7 @@
 		kfree(as);
 		return;
 	}
+	printk(KERN_INFO "usbaudio: registered dsp 14,%d\n", as->dev_audio);
 	/* everything successful */
 	list_add_tail(&as->list, &s->audiolist);
 }
@@ -3318,6 +3352,8 @@
 	state->chconfig = proc[8+proc[6]] | (proc[9+proc[6]] << 8);
 }
 
+
+/* See Audio Class Spec, section 4.3.2.5 */
 static void usb_audio_featureunit(struct consmixstate *state, unsigned char *ftr)
 {
 	struct mixerchannel *ch;
@@ -3335,7 +3371,7 @@
 	if (state->nrchannels > 2)
 		printk(KERN_WARNING "usbaudio: feature unit %u: OSS mixer interface does not support more than 2 channels\n", ftr[3]);
 	if (state->nrchannels == 1 && ftr[0] == 7+ftr[5]) {
-		printk(KERN_WARNING "usbaudio: workaround for broken Philips Camera Microphone descriptor enabled\n");
+		printk(KERN_DEBUG "usbaudio: workaround for Philips camera microphone descriptor enabled\n");
 		mchftr = ftr[6];
 		chftr = 0;
 	} else {
@@ -3465,7 +3501,7 @@
 		usb_audio_selectorunit(state, p1);
 		return;
 
-	case FEATURE_UNIT:
+	case FEATURE_UNIT: /* See USB Audio Class Spec 4.3.2.5 */
 		if (p1[0] < 7 || p1[0] < 7+p1[5]) {
 			printk(KERN_ERR "usbaudio: unit %u: invalid FEATURE_UNIT descriptor\n", unitid);
 			return;
@@ -3517,7 +3553,7 @@
 	state.buflen = buflen;
 	state.ctrlif = ctrlif;
 	set_bit(oterm[3], &state.unitbitmap);  /* mark terminal ID as visited */
-	printk(KERN_INFO "usbaudio: constructing mixer for Terminal %u type 0x%04x\n",
+	printk(KERN_DEBUG "usbaudio: constructing mixer for Terminal %u type 0x%04x\n",
 	       oterm[3], oterm[4] | (oterm[5] << 8));
 	usb_audio_recurseunit(&state, oterm[7]);
 	if (!state.nrmixch) {
@@ -3536,6 +3572,7 @@
 		kfree(ms);
 		return;
 	}
+	printk(KERN_INFO "usbaudio: registered mixer 14,%d\n", ms->dev_mixer);
 	list_add_tail(&ms->list, &s->mixerlist);
 }
 
@@ -3688,8 +3725,8 @@
 		return NULL;
 	}
 	ret = usb_get_descriptor(dev, USB_DT_CONFIG, i, buf, 8);
-	if (ret<0) {
-		printk(KERN_ERR "usbaudio: cannot get first 8 bytes of config descriptor %d of device %d\n", i, dev->devnum);
+	if (ret < 0) {
+		printk(KERN_ERR "usbaudio: cannot get first 8 bytes of config descriptor %d of device %d (error %d)\n", i, dev->devnum, ret);
 		return NULL;
 	}
 	if (buf[1] != USB_DT_CONFIG || buf[0] < 9) {
@@ -3702,7 +3739,7 @@
 	ret = usb_get_descriptor(dev, USB_DT_CONFIG, i, buffer, buflen);
 	if (ret < 0) {
 		kfree(buffer);
-		printk(KERN_ERR "usbaudio: cannot get config descriptor %d of device %d\n", i, dev->devnum);
+		printk(KERN_ERR "usbaudio: cannot get config descriptor %d of device %d (error %d)\n", i, dev->devnum, ret);
 		return NULL;
 	}
 	return usb_audio_parsecontrol(dev, buffer, buflen, ifnum);
@@ -3720,11 +3757,11 @@
 
 	/* we get called with -1 for every audiostreaming interface registered */
 	if (s == (struct usb_audio_state *)-1) {
-		dprintk((KERN_DEBUG "usb_audio_disconnect: called with -1\n"));
+		dprintk((KERN_DEBUG "usbaudio: note, usb_audio_disconnect called with -1\n"));
 		return;
 	}
 	if (!s->usbdev) {
-		dprintk((KERN_DEBUG "usb_audio_disconnect: already called for %p!\n", s));
+		dprintk((KERN_DEBUG "usbaudio: error,  usb_audio_disconnect already called for %p!\n", s));
 		return;
 	}
 	down(&open_sem);
@@ -3738,14 +3775,18 @@
 		usbout_disc(as);
 		wake_up(&as->usbin.dma.wait);
 		wake_up(&as->usbout.dma.wait);
-		if (as->dev_audio >= 0)
+		if (as->dev_audio >= 0) {
 			unregister_sound_dsp(as->dev_audio);
+			printk(KERN_INFO "usbaudio: unregister dsp 14,%d\n", as->dev_audio);
+		}
 		as->dev_audio = -1;
 	}
 	for(list = s->mixerlist.next; list != &s->mixerlist; list = list->next) {
 		ms = list_entry(list, struct usb_mixerdev, list);
-		if (ms->dev_mixer >= 0)
+		if (ms->dev_mixer >= 0) {
 			unregister_sound_mixer(ms->dev_mixer);
+			printk(KERN_INFO "usbaudio: unregister mixer 14,%d\n", ms->dev_mixer);
+		}
 		ms->dev_mixer = -1;
 	}
 	release(s);
@@ -3770,4 +3811,5 @@
 
 MODULE_AUTHOR( DRIVER_AUTHOR );
 MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL");
 
diff -urN linux-2.4.11-pre4/drivers/usb/hpusbscsi.c linux/drivers/usb/hpusbscsi.c
--- linux-2.4.11-pre4/drivers/usb/hpusbscsi.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/usb/hpusbscsi.c	Mon Dec  3 23:16:38 2001
@@ -0,0 +1,517 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+#include <linux/usb.h>
+#include <asm/atomic.h>
+#include <linux/blk.h>
+#include "../scsi/scsi.h"
+#include "../scsi/hosts.h"
+#include "../scsi/sd.h"
+
+#include "hpusbscsi.h"
+
+#define DEBUG(x...) \
+	printk( KERN_DEBUG x )
+
+static char *states[]={"FREE", "BEGINNING", "WORKING", "ERROR", "WAIT", "PREMATURE"};
+
+#define TRACE_STATE printk(KERN_DEBUG"hpusbscsi->state = %s at line %d\n", states[hpusbscsi->state], __LINE__)
+
+/* global variables */
+
+struct list_head hpusbscsi_devices;
+//LIST_HEAD(hpusbscsi_devices);
+
+/* USB related parts */
+
+static void *
+hpusbscsi_usb_probe (struct usb_device *dev, unsigned int interface,
+		     const struct usb_device_id *id)
+{
+	struct hpusbscsi *new;
+	struct usb_interface_descriptor *altsetting =
+		&(dev->actconfig->interface[interface].altsetting[0]);
+
+	int i, result;
+
+	/* basic check */
+
+	if (altsetting->bNumEndpoints != 3) {
+		printk (KERN_ERR "Wrong number of endpoints\n");
+		return NULL;
+	}
+
+	/* descriptor allocation */
+
+	new =
+		(struct hpusbscsi *) kmalloc (sizeof (struct hpusbscsi),
+					      GFP_KERNEL);
+	if (new == NULL)
+		return NULL;
+	DEBUG ("Allocated memory\n");
+	memset (new, 0, sizeof (struct hpusbscsi));
+	spin_lock_init (&new->dataurb.lock);
+	spin_lock_init (&new->controlurb.lock);
+	new->dev = dev;
+	init_waitqueue_head (&new->pending);
+	init_waitqueue_head (&new->deathrow);
+	INIT_LIST_HEAD (&new->lh);
+
+
+
+	/* finding endpoints */
+
+	for (i = 0; i < altsetting->bNumEndpoints; i++) {
+		if (
+		    (altsetting->endpoint[i].
+		     bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+		    USB_ENDPOINT_XFER_BULK) {
+			if (altsetting->endpoint[i].
+			    bEndpointAddress & USB_DIR_IN) {
+				new->ep_in =
+					altsetting->endpoint[i].
+					bEndpointAddress &
+					USB_ENDPOINT_NUMBER_MASK;
+			} else {
+				new->ep_out =
+					altsetting->endpoint[i].
+					bEndpointAddress &
+					USB_ENDPOINT_NUMBER_MASK;
+			}
+		} else {
+			new->ep_int =
+				altsetting->endpoint[i].
+				bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+			new->interrupt_interval= altsetting->endpoint[i].bInterval;
+		}
+	}
+
+	/* USB initialisation magic for the simple case */
+
+	result = usb_set_interface (dev, altsetting->bInterfaceNumber, 0);
+
+	switch (result) {
+	case 0:		/* no error */
+		break;
+
+	case -EPIPE:
+		usb_clear_halt (dev, usb_sndctrlpipe (dev, 0));
+		break;
+
+	default:
+		printk (KERN_ERR "unknown error %d from usb_set_interface\n",
+			 result);
+		goto err_out;
+	}
+
+	/* making a template for the scsi layer to fake detection of a scsi device */
+
+	memcpy (&(new->ctempl), &hpusbscsi_scsi_host_template,
+		sizeof (hpusbscsi_scsi_host_template));
+	(struct hpusbscsi *) new->ctempl.proc_dir = new;
+	new->ctempl.module = THIS_MODULE;
+
+	if (scsi_register_module (MODULE_SCSI_HA, &(new->ctempl)))
+		goto err_out;
+
+	/* adding to list for module unload */
+	list_add (&hpusbscsi_devices, &new->lh);
+
+	return new;
+
+      err_out:
+	kfree (new);
+	return NULL;
+}
+
+static void
+hpusbscsi_usb_disconnect (struct usb_device *dev, void *ptr)
+{
+                 usb_unlink_urb(&(((struct hpusbscsi *) ptr)->controlurb));
+	((struct hpusbscsi *) ptr)->dev = NULL;
+}
+
+static struct usb_device_id hpusbscsi_usb_ids[] = {
+	{USB_DEVICE (0x03f0, 0x0701)},	/* HP 53xx */
+	{USB_DEVICE (0x03f0, 0x0801)},	/* HP 7400 */
+	{USB_DEVICE (0x0638, 0x026a)},	/*Scan Dual II */
+	{}			/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, hpusbscsi_usb_ids);
+
+static struct usb_driver hpusbscsi_usb_driver = {
+	name:"hpusbscsi",
+	probe:hpusbscsi_usb_probe,
+	disconnect:hpusbscsi_usb_disconnect,
+	id_table:hpusbscsi_usb_ids,
+};
+
+/* module initialisation */
+
+int __init
+hpusbscsi_init (void)
+{
+	int result;
+
+	INIT_LIST_HEAD (&hpusbscsi_devices);
+	DEBUG ("Driver loaded\n");
+
+	if ((result = usb_register (&hpusbscsi_usb_driver)) < 0) {
+		printk (KERN_ERR "hpusbscsi: driver registration failed\n");
+		return -1;
+	} else {
+		return 0;
+	}
+}
+
+void __exit
+hpusbscsi_exit (void)
+{
+	struct list_head *tmp;
+	struct list_head *old;
+	struct hpusbscsi * o;
+
+	for (tmp = hpusbscsi_devices.next; tmp != &hpusbscsi_devices;/*nothing */) {
+		old = tmp;
+		tmp = tmp->next;
+		o = (struct hpusbscsi *)old;
+		usb_unlink_urb(&o->controlurb);
+		scsi_unregister_module(MODULE_SCSI_HA,&o->ctempl);
+		kfree(old);
+	}
+
+	usb_deregister (&hpusbscsi_usb_driver);
+}
+
+module_init (hpusbscsi_init);
+module_exit (hpusbscsi_exit);
+
+/* interface to the scsi layer */
+
+static int
+hpusbscsi_scsi_detect (struct SHT *sht)
+{
+	/* Whole function stolen from usb-storage */
+
+	struct hpusbscsi *desc = (struct hpusbscsi *) sht->proc_dir;
+	/* What a hideous hack! */
+
+	char local_name[48];
+
+
+	/* set up the name of our subdirectory under /proc/scsi/ */
+	sprintf (local_name, "hpusbscsi-%d", desc->number);
+	sht->proc_name = kmalloc (strlen (local_name) + 1, GFP_KERNEL);
+	/* FIXME: where is this freed ? */
+
+	if (!sht->proc_name) {
+		return 0;
+	}
+
+	strcpy (sht->proc_name, local_name);
+
+	sht->proc_dir = NULL;
+
+	/* build and submit an interrupt URB for status byte handling */
+ 	FILL_INT_URB(&desc->controlurb,
+			desc->dev,
+			usb_rcvintpipe(desc->dev,desc->ep_int),
+			&desc->scsi_state_byte,
+			1,
+			control_interrupt_callback,
+			desc,
+			desc->interrupt_interval
+	);
+
+	if ( 0  >  usb_submit_urb(&desc->controlurb)) {
+		kfree(sht->proc_name);
+		return 0;
+	}
+
+	/* In host->hostdata we store a pointer to desc */
+	desc->host = scsi_register (sht, sizeof (desc));
+	if (desc->host == NULL) {
+		kfree (sht->proc_name);
+		usb_unlink_urb(&desc->controlurb);
+		return 0;
+	}
+	desc->host->hostdata[0] = (unsigned long) desc;
+
+
+	return 1;
+}
+
+static int hpusbscsi_scsi_queuecommand (Scsi_Cmnd *srb, scsi_callback callback)
+{
+	struct hpusbscsi* hpusbscsi = (struct hpusbscsi*)(srb->host->hostdata[0]);
+	usb_urb_callback usb_callback;
+	int res;
+
+	hpusbscsi->use_count++;
+
+	/* we don't answer for anything but our single device on any faked host controller */
+	if ( srb->device->lun || srb->device->id || srb->device->channel ) {
+		if (callback) {
+			srb->result = DID_BAD_TARGET;
+			callback(srb);
+		}
+                	goto out;
+	}
+
+	/* Now we need to decide which callback to give to the urb we send the command with */
+
+	if (!srb->bufflen) {
+		usb_callback = simple_command_callback;
+	} else {
+        	if (srb->use_sg) {
+			usb_callback = scatter_gather_callback;
+			hpusbscsi->fragment = 0;
+		} else {
+                	usb_callback = simple_payload_callback;
+		}
+		/* Now we find out which direction data is to be transfered in */
+		hpusbscsi->current_data_pipe = DIRECTION_IS_IN(srb->cmnd[0]) ?
+			usb_rcvbulkpipe(hpusbscsi->dev, hpusbscsi->ep_in)
+		:
+			usb_sndbulkpipe(hpusbscsi->dev, hpusbscsi->ep_out)
+		;
+	}
+
+
+	TRACE_STATE;
+	if (hpusbscsi->state != HP_STATE_FREE) {
+		printk(KERN_CRIT"hpusbscsi - Ouch: queueing violation!\n");
+		return 1; /* This must not happen */
+	}
+
+        /* We zero the sense buffer to avoid confusing user space */
+        memset(srb->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
+
+	hpusbscsi->state = HP_STATE_BEGINNING;
+	TRACE_STATE;
+
+	/* We prepare the urb for writing out the scsi command */
+	FILL_BULK_URB(
+		&hpusbscsi->dataurb,
+		hpusbscsi->dev,
+		usb_sndbulkpipe(hpusbscsi->dev,hpusbscsi->ep_out),
+		srb->cmnd,
+		srb->cmd_len,
+		usb_callback,
+		hpusbscsi
+	);
+	hpusbscsi->scallback = callback;
+	hpusbscsi->srb = srb;
+
+	res = usb_submit_urb(&hpusbscsi->dataurb);
+	if (res) {
+		hpusbscsi->state = HP_STATE_FREE;
+		TRACE_STATE;
+		if (callback) {
+			srb->result = DID_ERROR;
+			callback(srb);
+		}
+	}
+
+out:
+	hpusbscsi->use_count--;
+	return 0;
+}
+
+static int hpusbscsi_scsi_host_reset (Scsi_Cmnd *srb)
+{
+	struct hpusbscsi* hpusbscsi = (struct hpusbscsi*)(srb->host->hostdata[0]);
+
+	printk(KERN_DEBUG"SCSI reset requested.\n");
+	usb_reset_device(hpusbscsi->dev);
+	printk(KERN_DEBUG"SCSI reset completed.\n");
+	hpusbscsi->state = HP_STATE_FREE;
+
+	return 0;
+}
+
+static int hpusbscsi_scsi_abort (Scsi_Cmnd *srb)
+{
+	struct hpusbscsi* hpusbscsi = (struct hpusbscsi*)(srb->host->hostdata[0]);
+	printk(KERN_DEBUG"Requested is canceled.\n");
+
+	usb_unlink_urb(&hpusbscsi->dataurb);
+	usb_unlink_urb(&hpusbscsi->controlurb);
+	hpusbscsi->state = HP_STATE_FREE;
+
+	return SCSI_ABORT_PENDING;
+}
+
+/* usb interrupt handlers - they are all running IN INTERRUPT ! */
+
+static void handle_usb_error (struct hpusbscsi *hpusbscsi)
+{
+	if (hpusbscsi->scallback != NULL) {
+		hpusbscsi->srb->result = DID_ERROR;
+		hpusbscsi->scallback(hpusbscsi->srb);
+	}
+	hpusbscsi->state = HP_STATE_FREE;
+}
+
+static void  control_interrupt_callback (struct urb *u)
+{
+	struct hpusbscsi * hpusbscsi = (struct hpusbscsi *)u->context;
+
+DEBUG("Getting status byte %d \n",hpusbscsi->scsi_state_byte);
+	if(u->status < 0) {
+                if (hpusbscsi->state != HP_STATE_FREE)
+                        handle_usb_error(hpusbscsi);
+		return;
+	}
+	hpusbscsi->srb->result &= SCSI_ERR_MASK;
+	hpusbscsi->srb->result |= hpusbscsi->scsi_state_byte<<1;
+
+	if (hpusbscsi->scallback != NULL && hpusbscsi->state == HP_STATE_WAIT)
+		/* we do a callback to the scsi layer if and only if all data has been transfered */
+		hpusbscsi->scallback(hpusbscsi->srb);
+
+	TRACE_STATE;
+	switch (hpusbscsi->state) {
+	case HP_STATE_WAIT:
+		hpusbscsi->state = HP_STATE_FREE;
+	TRACE_STATE;
+		break;
+	case HP_STATE_WORKING:
+	case HP_STATE_BEGINNING:
+		hpusbscsi->state = HP_STATE_PREMATURE;
+	TRACE_STATE;
+		break;
+	default:
+		printk(KERN_ERR"hpusbscsi: Unexpected status report.\n");
+	TRACE_STATE;
+		hpusbscsi->state = HP_STATE_FREE;
+	TRACE_STATE;
+		break;
+	}
+}
+
+static void simple_command_callback(struct urb *u)
+{
+	struct hpusbscsi * hpusbscsi = (struct hpusbscsi *)u->context;
+	if (u->status<0) {
+		handle_usb_error(hpusbscsi);
+		return;
+        }
+	TRACE_STATE;
+	if (hpusbscsi->state != HP_STATE_PREMATURE) {
+	        TRACE_STATE;
+		hpusbscsi->state = HP_STATE_WAIT;
+	} else {
+		if (hpusbscsi->scallback != NULL)
+			hpusbscsi->scallback(hpusbscsi->srb);
+		hpusbscsi->state = HP_STATE_FREE;
+	TRACE_STATE;
+	}
+}
+
+static void scatter_gather_callback(struct urb *u)
+{
+	struct hpusbscsi * hpusbscsi = (struct hpusbscsi *)u->context;
+        struct scatterlist *sg = hpusbscsi->srb->buffer;
+        usb_urb_callback callback;
+        int res;
+
+        DEBUG("Going through scatter/gather\n");
+        if (u->status < 0) {
+                handle_usb_error(hpusbscsi);
+                return;
+        }
+
+        if (hpusbscsi->fragment + 1 != hpusbscsi->srb->use_sg)
+                callback = scatter_gather_callback;
+        else
+                callback = simple_done;
+
+	TRACE_STATE;
+        if (hpusbscsi->state != HP_STATE_PREMATURE)
+		hpusbscsi->state = HP_STATE_WORKING;
+	TRACE_STATE;
+
+        FILL_BULK_URB(
+                u,
+                hpusbscsi->dev,
+                hpusbscsi->current_data_pipe,
+                sg[hpusbscsi->fragment].address,
+                sg[hpusbscsi->fragment++].length,
+                callback,
+                hpusbscsi
+        );
+
+        res = usb_submit_urb(u);
+        if (res)
+                hpusbscsi->state = HP_STATE_ERROR;
+	TRACE_STATE;
+}
+
+static void simple_done (struct urb *u)
+{
+	struct hpusbscsi * hpusbscsi = (struct hpusbscsi *)u->context;
+
+        if (u->status < 0) {
+                handle_usb_error(hpusbscsi);
+                return;
+        }
+        DEBUG("Data transfer done\n");
+	TRACE_STATE;
+	if (hpusbscsi->state != HP_STATE_PREMATURE) {
+		if (u->status < 0)
+			hpusbscsi->state = HP_STATE_ERROR;
+		else
+			hpusbscsi->state = HP_STATE_WAIT;
+		TRACE_STATE;
+	} else {
+		if (hpusbscsi->scallback != NULL)
+			hpusbscsi->scallback(hpusbscsi->srb);
+		hpusbscsi->state = HP_STATE_FREE;
+	}
+}
+
+static void simple_payload_callback (struct urb *u)
+{
+	struct hpusbscsi * hpusbscsi = (struct hpusbscsi *)u->context;
+	int res;
+
+	if (u->status<0) {
+                handle_usb_error(hpusbscsi);
+		return;
+        }
+
+	FILL_BULK_URB(
+		u,
+		hpusbscsi->dev,
+		hpusbscsi->current_data_pipe,
+		hpusbscsi->srb->buffer,
+		hpusbscsi->srb->bufflen,
+		simple_done,
+		hpusbscsi
+	);
+
+	res = usb_submit_urb(u);
+	if (res) {
+                handle_usb_error(hpusbscsi);
+		return;
+        }
+	TRACE_STATE;
+	if (hpusbscsi->state != HP_STATE_PREMATURE) {
+		hpusbscsi->state = HP_STATE_WORKING;
+	TRACE_STATE;
+	} else {
+		if (hpusbscsi->scallback != NULL)
+			hpusbscsi->scallback(hpusbscsi->srb);
+		hpusbscsi->state = HP_STATE_FREE;
+	TRACE_STATE;
+	}
+}
+
diff -urN linux-2.4.11-pre4/drivers/usb/hpusbscsi.h linux/drivers/usb/hpusbscsi.h
--- linux-2.4.11-pre4/drivers/usb/hpusbscsi.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/usb/hpusbscsi.h	Mon Dec  3 23:16:38 2001
@@ -0,0 +1,88 @@
+/* Header file for the hpusbscsi driver */
+/* (C) Copyright 2001 Oliver Neukum */
+/* sponsored by the Linux Usb Project */
+/* large parts based on or taken from code by John Fremlin and Matt Dharm */
+/* this file is licensed under the GPL */
+
+typedef void (*usb_urb_callback) (struct urb *);
+typedef void (*scsi_callback)(Scsi_Cmnd *);
+
+struct hpusbscsi
+{
+        struct list_head lh;
+        struct usb_device *dev; /* NULL indicates unplugged device */
+        int ep_out;
+        int ep_in;
+        int ep_int;
+        int interrupt_interval;
+
+        struct Scsi_Host *host;
+        Scsi_Host_Template ctempl;
+        int number;
+       scsi_callback scallback;
+       Scsi_Cmnd *srb;
+
+        int use_count;
+        wait_queue_head_t pending;
+        wait_queue_head_t deathrow;
+
+        struct urb dataurb;
+        struct urb controlurb;
+        int fragment;
+
+        int state;
+        int current_data_pipe;
+
+        u8 scsi_state_byte;
+};
+
+#define SCSI_ERR_MASK ~0x3fu
+
+static const unsigned char scsi_command_direction[256/8] = {
+	0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77,
+	0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+#define DIRECTION_IS_IN(x) ((scsi_command_direction[x>>3] >> (x & 7)) & 1)
+
+static int hpusbscsi_scsi_detect (struct SHT * sht);
+static void simple_command_callback(struct urb *u);
+static void scatter_gather_callback(struct urb *u);
+static void simple_payload_callback (struct urb *u);
+static void  control_interrupt_callback (struct urb *u);
+static void simple_done (struct urb *u);
+static int hpusbscsi_scsi_queuecommand (Scsi_Cmnd *srb, scsi_callback callback);
+static int hpusbscsi_scsi_host_reset (Scsi_Cmnd *srb);
+static int hpusbscsi_scsi_abort (Scsi_Cmnd *srb);
+
+static Scsi_Host_Template hpusbscsi_scsi_host_template = {
+	name:           "hpusbscsi",
+	detect:		hpusbscsi_scsi_detect,
+//	release:	hpusbscsi_scsi_release,
+	queuecommand:	hpusbscsi_scsi_queuecommand,
+
+	eh_abort_handler:	hpusbscsi_scsi_abort,
+	eh_host_reset_handler:	hpusbscsi_scsi_host_reset,
+
+	sg_tablesize:		SG_ALL,
+	can_queue:		1,
+	this_id:		-1,
+	cmd_per_lun:		1,
+	present:		0,
+	unchecked_isa_dma:	FALSE,
+	use_clustering:		TRUE,
+	use_new_eh_code:	TRUE,
+	emulated:		TRUE
+};
+
+/* defines for internal driver state */
+#define HP_STATE_FREE                 0  /*ready for next request */
+#define HP_STATE_BEGINNING      1  /*command being transfered */
+#define HP_STATE_WORKING         2  /* data transfer stage */
+#define HP_STATE_ERROR             3  /* error has been reported */
+#define HP_STATE_WAIT                 4  /* waiting for status transfer */
+#define HP_STATE_PREMATURE              5 /* status prematurely reported */
+
+
diff -urN linux-2.4.11-pre4/drivers/usb/ibmcam.c linux/drivers/usb/ibmcam.c
--- linux-2.4.11-pre4/drivers/usb/ibmcam.c	Tue Jul 24 14:20:56 2001
+++ linux/drivers/usb/ibmcam.c	Mon Dec  3 23:16:38 2001
@@ -27,58 +27,71 @@
 
 #include <linux/kernel.h>
 #include <linux/sched.h>
-#include <linux/list.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/smp_lock.h>
-#include <linux/videodev.h>
-#include <linux/vmalloc.h>
 #include <linux/wrapper.h>
 #include <linux/module.h>
 #include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/usb.h>
 
-#include <asm/io.h>
+#include "usbvideo.h"
 
-#include "ibmcam.h"
+#define	IBMCAM_VENDOR_ID	0x0545
+#define	IBMCAM_PRODUCT_ID	0x8080
+#define NETCAM_PRODUCT_ID	0x8002	/* IBM NetCamera, close to model 2 */
+
+#define MAX_IBMCAM		4	/* How many devices we allow to connect */
+#define USES_IBMCAM_PUTPIXEL    0       /* 0=Fast/oops 1=Slow/secure */
+
+/* Header signatures */
+
+/* Model 1 header: 00 FF 00 xx */
+#define HDRSIG_MODEL1_128x96	0x06	/* U Y V Y ... */
+#define HDRSIG_MODEL1_176x144	0x0e	/* U Y V Y ... */
+#define HDRSIG_MODEL1_352x288	0x00	/* V Y U Y ... */
+
+#define	IBMCAM_MODEL_1	1	/* XVP-501, 3 interfaces, rev. 0.02 */
+#define IBMCAM_MODEL_2	2	/* KSX-X9903, 2 interfaces, rev. 3.0a */
+#define IBMCAM_MODEL_3	3	/* KSX-X9902, 2 interfaces, rev. 3.01 */
+#define	IBMCAM_MODEL_4	4	/* IBM NetCamera, 0545/8002/3.0a */
+
+/* Video sizes supported */
+#define	VIDEOSIZE_128x96	VIDEOSIZE(128, 96)
+#define	VIDEOSIZE_176x144	VIDEOSIZE(176,144)
+#define	VIDEOSIZE_352x288	VIDEOSIZE(352,288)
+#define	VIDEOSIZE_320x240	VIDEOSIZE(320,240)
+#define	VIDEOSIZE_352x240	VIDEOSIZE(352,240)
+#define	VIDEOSIZE_640x480	VIDEOSIZE(640,480)
+#define	VIDEOSIZE_160x120	VIDEOSIZE(160,120)
+
+/* Video sizes supported */
+enum {
+	SIZE_128x96 = 0,
+	SIZE_160x120,
+	SIZE_176x144,
+	SIZE_320x240,
+	SIZE_352x240,
+	SIZE_352x288,
+	SIZE_640x480,
+	/* Add/remove/rearrange items before this line */
+	SIZE_LastItem
+};
 
 /*
- * Version Information
+ * This structure lives in uvd_t->user field.
  */
-#define DRIVER_VERSION "v1.0.0"
-#define DRIVER_AUTHOR "http://www.linux-usb.org/ibmcam/"
-#define DRIVER_DESC "IBM/Xirlink C-it USB Camera Driver for Linux (c) 2000"
-
-#define	ENABLE_HEXDUMP	0	/* Enable if you need it */
-static int debug = 0;
+typedef struct {
+	int initialized;	/* Had we already sent init sequence? */
+	int camera_model;	/* What type of IBM camera we got? */
+	int has_hdr;
+} ibmcam_t;
+#define	IBMCAM_T(uvd)	((ibmcam_t *)((uvd)->user_data))
 
-static int video_nr = -1;
+usbvideo_t *cams = NULL;
 
-/* Completion states of the data parser */
-typedef enum {
-	scan_Continue,		/* Just parse next item */
-	scan_NextFrame,		/* Frame done, send it to V4L */
-	scan_Out,		/* Not enough data for frame */
-	scan_EndParse		/* End parsing */
-} scan_state_t;
-
-/* Bit flags (options) */
-#define FLAGS_RETRY_VIDIOCSYNC		(1 << 0)
-#define	FLAGS_MONOCHROME		(1 << 1)
-#define FLAGS_DISPLAY_HINTS		(1 << 2)
-#define FLAGS_OVERLAY_STATS		(1 << 3)
-#define FLAGS_FORCE_TESTPATTERN		(1 << 4)
-#define FLAGS_SEPARATE_FRAMES		(1 << 5)
-#define FLAGS_CLEAN_FRAMES		(1 << 6)
+static int debug = 0;
 
 static int flags = 0; /* FLAGS_DISPLAY_HINTS | FLAGS_OVERLAY_STATS; */
 
-/* This is the size of V4L frame that we provide */
-static const int imgwidth = V4L_FRAME_WIDTH_USED;
-static const int imgheight = V4L_FRAME_HEIGHT;
-static const int min_imgwidth  = 8;
-static const int min_imgheight = 4;
+static const int min_canvasWidth  = 8;
+static const int min_canvasHeight = 4;
 
 static int lighting = 1; /* Medium */
 
@@ -88,27 +101,9 @@
 
 #define FRAMERATE_MIN	0
 #define FRAMERATE_MAX	6
-static int framerate = 2; /* Lower, reliable frame rate (8-12 fps) */
-
-enum {
-	VIDEOSIZE_128x96 = 0,
-	VIDEOSIZE_176x144,
-	VIDEOSIZE_352x288,
-	VIDEOSIZE_320x240,
-	VIDEOSIZE_352x240,
-};
-
-static int videosize = VIDEOSIZE_352x288;
+static int framerate = -1;
 
-/*
- * The value of 'scratchbufsize' affects quality of the picture
- * in many ways. Shorter buffers may cause loss of data when client
- * is too slow. Larger buffers are memory-consuming and take longer
- * to work with. This setting can be adjusted, but the default value
- * should be OK for most desktop users.
- */
-#define DEFAULT_SCRATCH_BUF_SIZE	(0x10000)	/* 64 KB */
-static const int scratchbufsize = DEFAULT_SCRATCH_BUF_SIZE;
+static int size = SIZE_352x288;
 
 /*
  * Here we define several initialization variables. They may
@@ -131,36 +126,37 @@
 static int hue_correction = 128;
 
 /* Settings for camera model 2 */
-static int init_model2_rg = -1;
 static int init_model2_rg2 = -1;
 static int init_model2_sat = -1;
 static int init_model2_yb = -1;
 
+/* 01.01.08 - Added for RCA video in support -LO */
+/* Settings for camera model 3 */
+static int init_model3_input = 0;
+
 MODULE_PARM(debug, "i");
 MODULE_PARM_DESC(debug, "Debug level: 0-9 (default=0)");
 MODULE_PARM(flags, "i");
-MODULE_PARM_DESC(flags, "Bitfield: 0=VIDIOCSYNC, 1=B/W, 2=show hints, 3=show stats, 4=test pattern, 5=seperate frames, 6=clean frames");
+MODULE_PARM_DESC(flags, "Bitfield: 0=VIDIOCSYNC, 1=B/W, 2=show hints, 3=show stats, 4=test pattern, 5=separate frames, 6=clean frames");
 MODULE_PARM(framerate, "i");
 MODULE_PARM_DESC(framerate, "Framerate setting: 0=slowest, 6=fastest (default=2)");
 MODULE_PARM(lighting, "i");
 MODULE_PARM_DESC(lighting, "Photosensitivity: 0=bright, 1=medium (default), 2=low light");
 MODULE_PARM(sharpness, "i");
 MODULE_PARM_DESC(sharpness, "Model1 noise reduction: 0=smooth, 6=sharp (default=4)");
-MODULE_PARM(videosize, "i");
-MODULE_PARM_DESC(videosize, "Image size: 0=128x96, 1=176x144, 2=352x288, 3=320x240, 4=352x240 (default=1)");
+MODULE_PARM(size, "i");
+MODULE_PARM_DESC(size, "Image size: 0=128x96 1=160x120 2=176x144 3=320x240 4=352x240 5=352x288 6=640x480  (default=5)");
 MODULE_PARM(init_brightness, "i");
 MODULE_PARM_DESC(init_brightness, "Brightness preconfiguration: 0-255 (default=128)");
 MODULE_PARM(init_contrast, "i");
 MODULE_PARM_DESC(init_contrast, "Contrast preconfiguration: 0-255 (default=192)");
 MODULE_PARM(init_color, "i");
-MODULE_PARM_DESC(init_color, "Dolor preconfiguration: 0-255 (default=128)");
+MODULE_PARM_DESC(init_color, "Color preconfiguration: 0-255 (default=128)");
 MODULE_PARM(init_hue, "i");
 MODULE_PARM_DESC(init_hue, "Hue preconfiguration: 0-255 (default=128)");
 MODULE_PARM(hue_correction, "i");
 MODULE_PARM_DESC(hue_correction, "YUV colorspace regulation: 0-255 (default=128)");
 
-MODULE_PARM(init_model2_rg, "i");
-MODULE_PARM_DESC(init_model2_rg, "Model2 preconfiguration: 0-255 (default=112)");
 MODULE_PARM(init_model2_rg2, "i");
 MODULE_PARM_DESC(init_model2_rg2, "Model2 preconfiguration: 0-255 (default=47)");
 MODULE_PARM(init_model2_sat, "i");
@@ -168,6 +164,14 @@
 MODULE_PARM(init_model2_yb, "i");
 MODULE_PARM_DESC(init_model2_yb, "Model2 preconfiguration: 0-255 (default=160)");
 
+/* 01.01.08 - Added for RCA video in support -LO */
+MODULE_PARM(init_model3_input, "i");
+MODULE_PARM_DESC(init_model3_input, "Model3 input: 0=CCD 1=RCA");
+
+MODULE_AUTHOR ("Dmitri");
+MODULE_DESCRIPTION ("IBM/Xirlink C-it USB Camera Driver for Linux (c) 2000");
+MODULE_LICENSE("GPL");
+
 /* Still mysterious i2c commands */
 static const unsigned short unknown_88 = 0x0088;
 static const unsigned short unknown_89 = 0x0089;
@@ -182,594 +186,326 @@
 static const unsigned short mod2_color_balance_rg2 = 0x001e;	/* 0 (red) .. $7F (green) */
 static const unsigned short mod2_saturation = 0x0020;		/* 0 (b/w) - $7F (full color) */
 static const unsigned short mod2_color_balance_yb = 0x0022;	/* 0..$7F, $50 is about right */
-static const unsigned short mod2_color_balance_rg = 0x0024;	/* 0..$7F, $70 is about right */
+static const unsigned short mod2_hue = 0x0024;			/* 0..$7F, $70 is about right */
 static const unsigned short mod2_sensitivity = 0x0028;		/* 0 (min) .. $1F (max) */
 
-#define MAX_IBMCAM	4
-
-struct usb_ibmcam cams[MAX_IBMCAM];
-
-/*******************************/
-/* Memory management functions */
-/*******************************/
-
-#define MDEBUG(x)	do { } while(0)		/* Debug memory management */
-
-static struct usb_driver ibmcam_driver;
-static void usb_ibmcam_release(struct usb_ibmcam *ibmcam);
-
-/* Given PGD from the address space's page table, return the kernel
- * virtual mapping of the physical memory mapped at ADR.
- */
-static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr)
-{
-	unsigned long ret = 0UL;
-	pmd_t *pmd;
-	pte_t *ptep, pte;
-
-	if (!pgd_none(*pgd)) {
-		pmd = pmd_offset(pgd, adr);
-		if (!pmd_none(*pmd)) {
-			ptep = pte_offset(pmd, adr);
-			pte = *ptep;
-			if (pte_present(pte)) {
-				ret = (unsigned long) page_address(pte_page(pte));
-				ret |= (adr & (PAGE_SIZE - 1));
-			}
-		}
-	}
-	MDEBUG(printk("uv2kva(%lx-->%lx)", adr, ret));
-	return ret;
-}
-
-static inline unsigned long uvirt_to_bus(unsigned long adr)
-{
-	unsigned long kva, ret;
-
-	kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr);
-	ret = virt_to_bus((void *)kva);
-	MDEBUG(printk("uv2b(%lx-->%lx)", adr, ret));
-	return ret;
-}
-
-static inline unsigned long kvirt_to_bus(unsigned long adr)
-{
-	unsigned long va, kva, ret;
-
-	va = VMALLOC_VMADDR(adr);
-	kva = uvirt_to_kva(pgd_offset_k(va), va);
-	ret = virt_to_bus((void *)kva);
-	MDEBUG(printk("kv2b(%lx-->%lx)", adr, ret));
-	return ret;
-}
-
-/* Here we want the physical address of the memory.
- * This is used when initializing the contents of the
- * area and marking the pages as reserved.
- */
-static inline unsigned long kvirt_to_pa(unsigned long adr)
-{
-	unsigned long va, kva, ret;
-
-	va = VMALLOC_VMADDR(adr);
-	kva = uvirt_to_kva(pgd_offset_k(va), va);
-	ret = __pa(kva);
-	MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret));
-	return ret;
-}
-
-static void *rvmalloc(unsigned long size)
-{
-	void *mem;
-	unsigned long adr, page;
-
-	/* Round it off to PAGE_SIZE */
-	size += (PAGE_SIZE - 1);
-	size &= ~(PAGE_SIZE - 1);
-
-	mem = vmalloc_32(size);
-	if (!mem)
-		return NULL;
-
-	memset(mem, 0, size); /* Clear the ram out, no junk to the user */
-	adr = (unsigned long) mem;
-	while (size > 0) {
-		page = kvirt_to_pa(adr);
-		mem_map_reserve(virt_to_page(__va(page)));
-		adr += PAGE_SIZE;
-		if (size > PAGE_SIZE)
-			size -= PAGE_SIZE;
-		else
-			size = 0;
-	}
-
-	return mem;
-}
-
-static void rvfree(void *mem, unsigned long size)
-{
-	unsigned long adr, page;
-
-	if (!mem)
-		return;
-
-	size += (PAGE_SIZE - 1);
-	size &= ~(PAGE_SIZE - 1);
-
-	adr=(unsigned long) mem;
-	while (size > 0) {
-		page = kvirt_to_pa(adr);
-		mem_map_unreserve(virt_to_page(__va(page)));
-		adr += PAGE_SIZE;
-		if (size > PAGE_SIZE)
-			size -= PAGE_SIZE;
-		else
-			size = 0;
-	}
-	vfree(mem);
-}
-
-#if ENABLE_HEXDUMP
-static void ibmcam_hexdump(const unsigned char *data, int len)
-{
-	char tmp[80];
-	int i, k;
-
-	for (i=k=0; len > 0; i++, len--) {
-		if (i > 0 && (i%16 == 0)) {
-			printk("%s\n", tmp);
-			k=0;
-		}
-		k += sprintf(&tmp[k], "%02x ", data[i]);
-	}
-	if (k > 0)
-		printk("%s\n", tmp);
-}
-#endif
-
-/*
- * usb_ibmcam_overlaychar()
- *
- * History:
- * 1/2/00   Created.
- */
-void usb_ibmcam_overlaychar(
-	struct usb_ibmcam *ibmcam,
-	struct ibmcam_frame *frame,
-	int x, int y, int ch)
-{
-	static const unsigned short digits[16] = {
-		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 */
-	};
-	unsigned short digit;
-	int ix, iy;
-
-	if ((ibmcam == NULL) || (frame == NULL))
-		return;
-
-	if (ch >= '0' && ch <= '9')
-		ch -= '0';
-	else if (ch >= 'A' && ch <= 'F')
-		ch = 10 + (ch - 'A');
-	else if (ch >= 'a' && ch <= 'f')
-		ch = 10 + (ch - 'a');
-	else
-		return;
-	digit = digits[ch];
-
-	for (iy=0; iy < 5; iy++) {
-		for (ix=0; ix < 3; ix++) {
-			if (digit & 0x8000) {
-				IBMCAM_PUTPIXEL(frame, x+ix, y+iy, 0xFF, 0xFF, 0xFF);
-			}
-			digit = digit << 1;
-		}
-	}
-}
+struct struct_initData {
+	unsigned char req;
+	unsigned short value;
+	unsigned short index;
+};
 
 /*
- * usb_ibmcam_overlaystring()
+ * ibmcam_size_to_videosize()
  *
- * History:
- * 1/2/00   Created.
+ * This procedure converts module option 'size' into the actual
+ * videosize_t that defines the image size in pixels. We need
+ * simplified 'size' because user wants a simple enumerated list
+ * of choices, not an infinite set of possibilities.
  */
-void usb_ibmcam_overlaystring(
-	struct usb_ibmcam *ibmcam,
-	struct ibmcam_frame *frame,
-	int x, int y, const char *str)
+static videosize_t ibmcam_size_to_videosize(int size)
 {
-	while (*str) {
-		usb_ibmcam_overlaychar(ibmcam, frame, x, y, *str);
-		str++;
-		x += 4; /* 3 pixels character + 1 space */
+	videosize_t vs = VIDEOSIZE_352x288;
+	RESTRICT_TO_RANGE(size, 0, (SIZE_LastItem-1));
+	switch (size) {
+	case SIZE_128x96:
+		vs = VIDEOSIZE_128x96;
+		break;
+	case SIZE_160x120:
+		vs = VIDEOSIZE_160x120;
+		break;
+	case SIZE_176x144:
+		vs = VIDEOSIZE_176x144;
+		break;
+	case SIZE_320x240:
+		vs = VIDEOSIZE_320x240;
+		break;
+	case SIZE_352x240:
+		vs = VIDEOSIZE_352x240;
+		break;
+	case SIZE_352x288:
+		vs = VIDEOSIZE_352x288;
+		break;
+	case SIZE_640x480:
+		vs = VIDEOSIZE_640x480;
+		break;
+	default:
+		err("size=%d. is not valid", size);
+		break;
 	}
+	return vs;
 }
 
 /*
- * usb_ibmcam_overlaystats()
- *
- * Overlays important debugging information.
- *
- * History:
- * 1/2/00   Created.
- */
-void usb_ibmcam_overlaystats(struct usb_ibmcam *ibmcam, struct ibmcam_frame *frame)
-{
-	const int y_diff = 8;
-	char tmp[16];
-	int x = 10;
-	int y = 10;
-
-	sprintf(tmp, "%8x", ibmcam->frame_num);
-	usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
-	y += y_diff;
-
-	sprintf(tmp, "%8lx", ibmcam->urb_count);
-	usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
-	y += y_diff;
-
-	sprintf(tmp, "%8lx", ibmcam->urb_length);
-	usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
-	y += y_diff;
-
-	sprintf(tmp, "%8lx", ibmcam->data_count);
-	usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
-	y += y_diff;
-
-	sprintf(tmp, "%8lx", ibmcam->header_count);
-	usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
-	y += y_diff;
-
-	sprintf(tmp, "%8lx", ibmcam->scratch_ovf_count);
-	usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
-	y += y_diff;
-
-	sprintf(tmp, "%8lx", ibmcam->iso_skip_count);
-	usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
-	y += y_diff;
-
-	sprintf(tmp, "%8lx", ibmcam->iso_err_count);
-	usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
-	y += y_diff;
-
-	sprintf(tmp, "%8x", ibmcam->vpic.colour);
-	usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
-	y += y_diff;
-
-	sprintf(tmp, "%8x", ibmcam->vpic.hue);
-	usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
-	y += y_diff;
-
-	sprintf(tmp, "%8x", ibmcam->vpic.brightness >> 8);
-	usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
-	y += y_diff;
-
-	sprintf(tmp, "%8x", ibmcam->vpic.contrast >> 12);
-	usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
-	y += y_diff;
-
-	sprintf(tmp, "%8d", ibmcam->vpic.whiteness >> 8);
-	usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
-	y += y_diff;
-}
-
-/*
- * usb_ibmcam_testpattern()
+ * ibmcam_find_header()
  *
- * Procedure forms a test pattern (yellow grid on blue background).
- *
- * Parameters:
- * fullframe:   if TRUE then entire frame is filled, otherwise the procedure
- *	      continues from the current scanline.
- * pmode	0: fill the frame with solid blue color (like on VCR or TV)
- *	      1: Draw a colored grid
+ * Locate one of supported header markers in the queue.
+ * Once found, remove all preceding bytes AND the marker (4 bytes)
+ * from the data pump queue. Whatever follows must be video lines.
  *
  * History:
- * 1/2/00   Created.
+ * 1/21/00  Created.
  */
-void usb_ibmcam_testpattern(struct usb_ibmcam *ibmcam, int fullframe, int pmode)
+static ParseState_t ibmcam_find_header(uvd_t *uvd) /* FIXME: Add frame here */
 {
-	static const char proc[] = "usb_ibmcam_testpattern";
-	struct ibmcam_frame *frame;
-	unsigned char *f;
-	int num_cell = 0;
-	int scan_length = 0;
-	static int num_pass = 0;
-
-	if (ibmcam == NULL) {
-		printk(KERN_ERR "%s: ibmcam == NULL\n", proc);
-		return;
-	}
-	if ((ibmcam->curframe < 0) || (ibmcam->curframe >= IBMCAM_NUMFRAMES)) {
-		printk(KERN_ERR "%s: ibmcam->curframe=%d.\n", proc, ibmcam->curframe);
-		return;
-	}
+	usbvideo_frame_t *frame;
+	ibmcam_t *icam;
 
-	/* Grab the current frame */
-	frame = &ibmcam->frame[ibmcam->curframe];
-
-	/* Optionally start at the beginning */
-	if (fullframe) {
-		frame->curline = 0;
-		frame->scanlength = 0;
+	if ((uvd->curframe) < 0 || (uvd->curframe >= USBVIDEO_NUMFRAMES)) {
+		err("ibmcam_find_header: Illegal frame %d.", uvd->curframe);
+		return scan_EndParse;
 	}
-
-	/* Form every scan line */
-	for (; frame->curline < imgheight; frame->curline++) {
-		int i;
-
-		f = frame->data + (imgwidth * 3 * frame->curline);
-		for (i=0; i < imgwidth; i++) {
-			unsigned char cb=0x80;
-			unsigned char cg = 0;
-			unsigned char cr = 0;
-
-			if (pmode == 1) {
-				if (frame->curline % 32 == 0)
-					cb = 0, cg = cr = 0xFF;
-				else if (i % 32 == 0) {
-					if (frame->curline % 32 == 1)
-						num_cell++;
-					cb = 0, cg = cr = 0xFF;
-				} else {
-					cb = ((num_cell*7) + num_pass) & 0xFF;
-					cg = ((num_cell*5) + num_pass*2) & 0xFF;
-					cr = ((num_cell*3) + num_pass*3) & 0xFF;
+	icam = IBMCAM_T(uvd);
+	assert(icam != NULL);
+	frame = &uvd->frame[uvd->curframe];
+	icam->has_hdr = 0;
+	switch (icam->camera_model) {
+	case IBMCAM_MODEL_1:
+	{
+		const int marker_len = 4;
+		while (RingQueue_GetLength(&uvd->dp) >= marker_len) {
+			if ((RING_QUEUE_PEEK(&uvd->dp, 0) == 0x00) &&
+			    (RING_QUEUE_PEEK(&uvd->dp, 1) == 0xFF) &&
+			    (RING_QUEUE_PEEK(&uvd->dp, 2) == 0x00))
+			{
+#if 0				/* This code helps to detect new frame markers */
+				info("Header sig: 00 FF 00 %02X", RING_QUEUE_PEEK(&uvd->dp, 3));
+#endif
+				frame->header = RING_QUEUE_PEEK(&uvd->dp, 3);
+				if ((frame->header == HDRSIG_MODEL1_128x96) ||
+				    (frame->header == HDRSIG_MODEL1_176x144) ||
+				    (frame->header == HDRSIG_MODEL1_352x288))
+				{
+#if 0
+					info("Header found.");
+#endif
+					RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, marker_len);
+					icam->has_hdr = 1;
+					break;
 				}
-			} else {
-				/* Just the blue screen */
 			}
-				
-			*f++ = cb;
-			*f++ = cg;
-			*f++ = cr;
-			scan_length += 3;
+			/* If we are still here then this doesn't look like a header */
+			RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 1);
 		}
+		break;
 	}
-
-	frame->grabstate = FRAME_DONE;
-	frame->scanlength += scan_length;
-	++num_pass;
-
-	/* We do this unconditionally, regardless of FLAGS_OVERLAY_STATS */
-	usb_ibmcam_overlaystats(ibmcam, frame);
-}
-
-static unsigned char *ibmcam_model1_find_header(unsigned char hdr_sig, unsigned char *data, int len)
-{
-	while (len >= 4)
+	case IBMCAM_MODEL_2:
+case IBMCAM_MODEL_4:
 	{
-		if ((data[0] == 0x00) && (data[1] == 0xFF) && (data[2] == 0x00))
-		{
+		int marker_len = 0;
+		switch (uvd->videosize) {
+		case VIDEOSIZE_176x144:
+			marker_len = 10;
+			break;
+		default:
+			marker_len = 2;
+			break;
+		}
+		while (RingQueue_GetLength(&uvd->dp) >= marker_len) {
+			if ((RING_QUEUE_PEEK(&uvd->dp, 0) == 0x00) &&
+			    (RING_QUEUE_PEEK(&uvd->dp, 1) == 0xFF))
+			{
 #if 0
-			/* This code helps to detect new frame markers */
-			printk(KERN_DEBUG "Header sig: 00 FF 00 %02X\n", data[3]);
+				info("Header found.");
 #endif
-			if (data[3] == hdr_sig) {
-				if (debug > 2)
-					printk(KERN_DEBUG "Header found.\n");
-				return data+4;
+				RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, marker_len);
+				icam->has_hdr = 1;
+				frame->header = HDRSIG_MODEL1_176x144;
+				break;
 			}
+			/* If we are still here then this doesn't look like a header */
+			RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 1);
 		}
-		++data;
-		--len;
-	}
-	return NULL;
-}
-
-static unsigned char *ibmcam_model2_find_header(unsigned char hdr_sig, unsigned char *data, int len)
-{
-	int marker_len = 0;
-
-	switch (videosize) {
-	case VIDEOSIZE_176x144:
-		marker_len = 10;
-		break;
-	default:
-		marker_len = 2;
 		break;
 	}
-	while (len >= marker_len)
-	{
-		if ((data[0] == 0x00) && (data[1] == 0xFF))
-		{
+	case IBMCAM_MODEL_3:
+	{	/*
+		 * Headers: (one precedes every frame). nc=no compression,
+		 * bq=best quality bf=best frame rate.
+		 *
+		 * 176x144: 00 FF 02 { 0A=nc CA=bq EA=bf }
+		 * 320x240: 00 FF 02 { 08=nc 28=bq 68=bf }
+		 * 640x480: 00 FF 03 { 08=nc 28=bq 68=bf }
+		 *
+		 * Bytes '00 FF' seem to indicate header. Other two bytes
+		 * encode the frame type. This is a set of bit fields that
+		 * encode image size, compression type etc. These fields
+		 * do NOT contain frame number because all frames carry
+		 * the same header.
+		 */
+		const int marker_len = 4;
+		while (RingQueue_GetLength(&uvd->dp) >= marker_len) {
+			if ((RING_QUEUE_PEEK(&uvd->dp, 0) == 0x00) &&
+			    (RING_QUEUE_PEEK(&uvd->dp, 1) == 0xFF) &&
+			    (RING_QUEUE_PEEK(&uvd->dp, 2) != 0xFF))
+			{
+				/*
+				 * Combine 2 bytes of frame type into one
+				 * easy to use value
+				 */
+				unsigned long byte3, byte4;
+
+				byte3 = RING_QUEUE_PEEK(&uvd->dp, 2);
+				byte4 = RING_QUEUE_PEEK(&uvd->dp, 3);
+				frame->header = (byte3 << 8) | byte4;
 #if 0
-			/* This code helps to detect new frame markers */
-			static int pass = 0;
-			if (pass++ == 0)
-				ibmcam_hexdump(data, (len > 16) ? 16 : len);
+				info("Header found.");
 #endif
-			if (debug > 2)
-				printk(KERN_DEBUG "Header found.\n");
-			return data+marker_len;
+				RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, marker_len);
+				icam->has_hdr = 1;
+				break;
+			}
+			/* If we are still here then this doesn't look like a header */
+			RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 1);
 		}
-		++data;
-		--len;
+		break;
 	}
-	return NULL;
-}
-
-/* How much data is left in the scratch buf? */
-#define scratch_left(x)	(ibmcam->scratchlen - (int)((char *)x - (char *)ibmcam->scratch))
-
-/* Grab the remaining */
-static void usb_ibmcam_align_scratch(struct usb_ibmcam *ibmcam, unsigned char *data)
-{
-	unsigned long left;
-
-	left = scratch_left(data);
-	memmove(ibmcam->scratch, data, left);
-	ibmcam->scratchlen = left;
-}
-
-/*
- * usb_ibmcam_find_header()
- *
- * Locate one of supported header markers in the scratch buffer.
- * Once found, remove all preceding bytes AND the marker (4 bytes)
- * from the scratch buffer. Whatever follows must be video lines.
- *
- * History:
- * 1/21/00  Created.
- */
-static scan_state_t usb_ibmcam_find_header(struct usb_ibmcam *ibmcam)
-{
-	struct ibmcam_frame *frame;
-	unsigned char *data, *tmp;
-
-	data = ibmcam->scratch;
-	frame = &ibmcam->frame[ibmcam->curframe];
-
-	if (ibmcam->camera_model == IBMCAM_MODEL_1)
-		tmp = ibmcam_model1_find_header(frame->hdr_sig, data, scratch_left(data));
-	else if (ibmcam->camera_model == IBMCAM_MODEL_2)
-		tmp = ibmcam_model2_find_header(frame->hdr_sig, data, scratch_left(data));
-	else
-		tmp = NULL;
-
-	if (tmp == NULL) {
-		/* No header - entire scratch buffer is useless! */
-		if (debug > 2)
-			printk(KERN_DEBUG "Skipping frame, no header\n");
-		ibmcam->scratchlen = 0;
+	default:
+		break;
+	}
+	if (!icam->has_hdr) {
+		if (uvd->debug > 2)
+			info("Skipping frame, no header");
 		return scan_EndParse;
 	}
-	/* Header found */
-	data = tmp;
 
-	ibmcam->has_hdr = 1;
-	ibmcam->header_count++;
-	frame->scanstate = STATE_LINES;
+	/* Header found */
+	icam->has_hdr = 1;
+	uvd->stats.header_count++;
+	frame->scanstate = ScanState_Lines;
 	frame->curline = 0;
 
 	if (flags & FLAGS_FORCE_TESTPATTERN) {
-		usb_ibmcam_testpattern(ibmcam, 1, 1);
+		usbvideo_TestPattern(uvd, 1, 1);
 		return scan_NextFrame;
 	}
-	usb_ibmcam_align_scratch(ibmcam, data);
 	return scan_Continue;
 }
 
 /*
- * usb_ibmcam_parse_lines()
+ * ibmcam_parse_lines()
  *
- * Parse one line (TODO: more than one!) from the scratch buffer, put
- * decoded RGB value into the current frame buffer and add the written
- * number of bytes (RGB) to the *pcopylen.
+ * Parse 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.
  *
  * History:
- * 1/21/00  Created.
+ * 21-Jan-2000 Created.
+ * 12-Oct-2000 Reworked to reflect interlaced nature of the data.
  */
-static scan_state_t usb_ibmcam_parse_lines(struct usb_ibmcam *ibmcam, long *pcopylen)
+static ParseState_t ibmcam_parse_lines(
+	uvd_t *uvd,
+	usbvideo_frame_t *frame,
+	long *pcopylen)
 {
-	struct ibmcam_frame *frame;
-	unsigned char *data, *f, *chromaLine;
-	unsigned int len;
-	const int v4l_linesize = imgwidth * V4L_BYTES_PER_PIXEL;	/* V4L line offset */
-	const int hue_corr  = (ibmcam->vpic.hue - 0x8000) >> 10;	/* -32..+31 */
+	unsigned char *f;
+	ibmcam_t *icam;
+	unsigned int len, scanLength, scanHeight, order_uv, order_yc;
+	int v4l_linesize; /* V4L line offset */
+	const int hue_corr  = (uvd->vpic.hue - 0x8000) >> 10;	/* -32..+31 */
 	const int hue2_corr = (hue_correction - 128) / 4;		/* -32..+31 */
 	const int ccm = 128; /* Color correction median - see below */
-	int y, u, v, i, frame_done=0, mono_plane, color_corr;
-
-	color_corr = (ibmcam->vpic.colour - 0x8000) >> 8; /* -128..+127 = -ccm..+(ccm-1)*/
+	int y, u, v, i, frame_done=0, color_corr;
+	static unsigned char lineBuffer[640*3];
+	unsigned const char *chromaLine, *lumaLine;
+
+	assert(uvd != NULL);
+	assert(frame != NULL);
+	icam = IBMCAM_T(uvd);
+	assert(icam != NULL);
+	color_corr = (uvd->vpic.colour - 0x8000) >> 8; /* -128..+127 = -ccm..+(ccm-1)*/
 	RESTRICT_TO_RANGE(color_corr, -ccm, ccm+1);
-	data = ibmcam->scratch;
-	frame = &ibmcam->frame[ibmcam->curframe];
 
-	len = frame->frmwidth * 3; /* 1 line of mono + 1 line of color */
-	/*printk(KERN_DEBUG "len=%d. left=%d.\n",len,scratch_left(data));*/
+	v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL;
+
+	if (IBMCAM_T(uvd)->camera_model == IBMCAM_MODEL_4) {
+		/* Model 4 frame markers do not carry image size identification */
+		switch (uvd->videosize) {
+		case VIDEOSIZE_128x96:
+		case VIDEOSIZE_160x120:
+		case VIDEOSIZE_176x144:
+			scanLength = VIDEOSIZE_X(uvd->videosize);
+			scanHeight = VIDEOSIZE_Y(uvd->videosize);
+			break;
+		default:
+			err("ibmcam_parse_lines: Wrong mode.");
+			return scan_Out;
+		}
+		order_yc = 1;	/* order_yc: true=Yc false=cY ('c'=either U or V) */
+		order_uv = 1;	/* Always true in this algorithm */
+	} else {
+		switch (frame->header) {
+		case HDRSIG_MODEL1_128x96:
+			scanLength = 128;
+			scanHeight = 96;
+			order_uv = 1;	/* U Y V Y ... */
+			break;
+		case HDRSIG_MODEL1_176x144:
+			scanLength = 176;
+			scanHeight = 144;
+			order_uv = 1;	/* U Y V Y ... */
+			break;
+		case HDRSIG_MODEL1_352x288:
+			scanLength = 352;
+			scanHeight = 288;
+			order_uv = 0;	/* Y V Y V ... */
+			break;
+		default:
+			err("Unknown header signature 00 FF 00 %02lX", frame->header);
+			return scan_NextFrame;
+		}
+		/* order_yc: true=Yc false=cY ('c'=either U or V) */
+		order_yc = (IBMCAM_T(uvd)->camera_model == IBMCAM_MODEL_2);
+	}
 
-	mono_plane = ((frame->curline & 1) == 0);
+	len = scanLength * 3;
+	assert(len <= sizeof(lineBuffer));
 
 	/*
-	 * Lines are organized this way (or are they?)
+	 * Lines are organized this way:
 	 *
 	 * I420:
 	 * ~~~~
+	 * <scanLength->
 	 * ___________________________________
 	 * |-----Y-----|---UVUVUV...UVUV-----| \
 	 * |-----------+---------------------|  \
-	 * |<-- 176 -->|<------ 176*2 ------>|  Total 72. pairs of lines
-	 * |...	   ...	     ...|  /
-	 * |___________|_____________________| /
-	 *  - odd line- ------- even line ---
-	 *
-	 * another format:
-	 * ~~~~~~~~~~~~~~
-	 * ___________________________________
-	 * |-----Y-----|---UVUVUV...UVUV-----| \
-	 * |-----------+---------------------|  \
-	 * |<-- 352 -->|<------ 352*2 ------>|  Total 144. pairs of lines
-	 * |...	   ...	     ...|  /
+	 * |<-- 176 -->|<------ 176*2 ------>|  Total 72. lines (interlaced)
+	 * |...	   ... |        ...          |  /
+	 * |<-- 352 -->|<------ 352*2 ------>|  Total 144. lines (interlaced)
 	 * |___________|_____________________| /
-	 *  - odd line- ------- even line ---
+	 *  \           \
+	 *   lumaLine    chromaLine
 	 */
 
 	/* Make sure there's enough data for the entire line */
-	if (scratch_left(data) < (len+1024)) {
-		/*printk(KERN_DEBUG "out of data, need %u.\n", len);*/
+	if (RingQueue_GetLength(&uvd->dp) < len)
 		return scan_Out;
-	}
 
-#if 0
-	{	/* This code prints beginning of the source frame */
-		static int pass = 0;
-		if ((pass++ % 3000) == 0)
-			ibmcam_hexdump(data, 16);
-	}
-#endif
+	/* Suck one line out of the ring queue */
+	RingQueue_Dequeue(&uvd->dp, lineBuffer, len);
 
-#if 0
-	if (frame->curline == 10 || frame->curline == 11) {
-		/* This code prints beginning of 10th (mono), 11th (chroma) line */
-		static int pass = 0;
-		if ((pass % 100) == 0)
-			ibmcam_hexdump(data, 16);
-		if (frame->curline == 11)
-			pass++;
-	}
-#endif
 	/*
 	 * 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 + 1) >= V4L_FRAME_HEIGHT)
+	if ((frame->curline + 2) >= VIDEOSIZE_Y(frame->request))
 		return scan_NextFrame;
 
 	/*
-	 * Now we are sure that entire line (representing all 'frame->frmwidth'
-	 * pixels from the camera) is available in the scratch buffer. We
-	 * start copying the line left-aligned to the V4L buffer (which
-	 * might be larger - not smaller, hopefully). If the camera
-	 * line is shorter then we should pad the V4L buffer with something
-	 * (black in this case) to complete the line.
+	 * 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);
 	f = frame->data + (v4l_linesize * frame->curline);
 
 	/*
-	 * chromaLine points to 1st pixel of the line with chrominance.
-	 * If current line is monochrome then chromaLine points to next
-	 * line after monochrome one. If current line has chrominance
-	 * then chromaLine points to this very line. Such method allows
-	 * to access chrominance data uniformly.
-	 *
 	 * To obtain chrominance data from the 'chromaLine' use this:
 	 *   v = chromaLine[0]; // 0-1:[0], 2-3:[4], 4-5:[8]...
 	 *   u = chromaLine[2]; // 0-1:[2], 2-3:[6], 4-5:[10]...
@@ -778,40 +514,16 @@
 	 * v_index = (i >> 1) << 2;
 	 * u_index = (i >> 1) << 2 + 2;
 	 *
-	 * where 'i' is the column number [0..frame->frmwidth-1]
+	 * where 'i' is the column number [0..VIDEOSIZE_X(frame->request)-1]
 	 */
-	chromaLine = data;
-	if (mono_plane)
-		chromaLine += frame->frmwidth;
-
-	for (i = 0; i < frame->frmwidth; i++, data += (mono_plane ? 1 : 2))
+	lumaLine = lineBuffer;
+	chromaLine = lineBuffer + scanLength;
+	for (i = 0; i < VIDEOSIZE_X(frame->request); i++)
 	{
 		unsigned char rv, gv, bv;	/* RGB components */
 
-		/*
-		 * Search for potential Start-Of-Frame marker. It should
-		 * not be here, of course, but if your formats don't match
-		 * you might exceed the frame. We must match the marker to
-		 * each byte of multi-byte data element if it is multi-byte.
-		 */
-#if 1
-		if ((ibmcam->camera_model == IBMCAM_MODEL_1) && (scratch_left(data) >= (4+2))) {
-			unsigned char *dp;
-			int j;
-
-			for (j=0, dp=data; j < 2; j++, dp++) {
-				if ((dp[0] == 0x00) && (dp[1] == 0xFF) &&
-				    (dp[2] == 0x00) && (dp[3] == frame->hdr_sig)) {
-					ibmcam->has_hdr = 2;
-					frame_done++;
-					break;
-				}
-			}
-		}
-#endif
-
 		/* Check for various visual debugging hints (colorized pixels) */
-		if ((flags & FLAGS_DISPLAY_HINTS) && (ibmcam->has_hdr)) {
+		if ((flags & FLAGS_DISPLAY_HINTS) && (icam->has_hdr)) {
 			/*
 			 * This is bad and should not happen. This means that
 			 * we somehow overshoot the line and encountered new
@@ -819,7 +531,7 @@
 			 * of whack. This cyan dot will help you to figure
 			 * out where exactly the new frame arrived.
 			 */
-			if (ibmcam->has_hdr == 1) {
+			if (icam->has_hdr == 1) {
 				bv = 0; /* Yellow marker */
 				gv = 0xFF;
 				rv = 0xFF;
@@ -828,15 +540,27 @@
 				gv = 0xFF;
 				rv = 0;
 			}
-			ibmcam->has_hdr = 0;
+			icam->has_hdr = 0;
 			goto make_pixel;
 		}
 
-		if (mono_plane || frame->order_yc)
-			y = data[0];
-		else
-			y = data[1];
+		/*
+		 * Check if we are still in range. We may be out of range if our
+		 * V4L canvas is wider or taller than the camera "native" image.
+		 * Then we quickly fill the remainder of the line with zeros to
+		 * make black color and quit the horizontal scanning loop.
+		 */
+		if (((frame->curline + 2) >= scanHeight) || (i >= scanLength)) {
+			const int j = i * V4L_BYTES_PER_PIXEL;
+#if USES_IBMCAM_PUTPIXEL
+			/* Refresh 'f' because we don't use it much with PUTPIXEL */
+			f = frame->data + (v4l_linesize * frame->curline) + j;
+#endif
+			memset(f, 0, v4l_linesize - j);
+			break;
+		}
 
+		y = lumaLine[i];
 		if (flags & FLAGS_MONOCHROME) /* Use monochrome for debugging */
 			rv = gv = bv = y;
 		else {
@@ -845,11 +569,11 @@
 			off_0 = (i >> 1) << 2;
 			off_2 = off_0 + 2;
 
-			if (frame->order_yc) {
+			if (order_yc) {
 				off_0++;
 				off_2++;
 			}
-			if (!frame->order_uv) {
+			if (!order_uv) {
 				off_0 += 2;
 				off_2 -= 2;
 			}
@@ -877,7 +601,7 @@
 		 * (in this order).
 		 */
 #if USES_IBMCAM_PUTPIXEL
-		IBMCAM_PUTPIXEL(frame, i, frame->curline, rv, gv, bv);
+		RGB24_PUTPIXEL(frame, i, frame->curline, rv, gv, bv);
 #else
 		*f++ = bv;
 		*f++ = gv;
@@ -899,18 +623,19 @@
 	 * may fill more than one output scanline if we do vertical
 	 * enlargement.
 	 */
-	frame->curline++;
-	*pcopylen += v4l_linesize;
-	usb_ibmcam_align_scratch(ibmcam, data);
+	frame->curline += 2;
+	if (pcopylen != NULL)
+		*pcopylen += 2 * v4l_linesize;
+	frame->deinterlace = Deinterlace_FillOddLines;
 
-	if (frame_done || (frame->curline >= frame->frmheight))
+	if (frame_done || (frame->curline >= VIDEOSIZE_Y(frame->request)))
 		return scan_NextFrame;
 	else
 		return scan_Continue;
 }
 
 /*
- * usb_ibmcam_model2_parse_lines()
+ * ibmcam_model2_320x240_parse_lines()
  *
  * This procedure deals with a weird RGB format that is produced by IBM
  * camera model 2 in modes 320x240 and above; 'x' below is 159 or 175,
@@ -918,15 +643,15 @@
  *
  * <--- 160 or 176 pairs of RA,RB bytes ----->
  * *-----------------------------------------* \
- * | RA0 | RB0 | RA1 | RB1 | ... | RAx | RBx |  \
- * |-----+-----+-----+-----+ ... +-----+-----|   *- This is pair of horizontal lines,
- * | B0  | G0  | B1  | G1  | ... | Bx  | Gx  |  /   total 240 or 288 lines (120 or 144
- * |=====+=====+=====+=====+ ... +=====+=====| /    such pairs).
+ * | RA0 | RB0 | RA1 | RB1 | ... | RAx | RBx |  \   This is pair of horizontal lines,
+ * |-----+-----+-----+-----+ ... +-----+-----|   *- or one interlaced line, total
+ * | B0  | G0  | B1  | G1  | ... | Bx  | Gx  |  /   120 or 144 such pairs which yield
+ * |=====+=====+=====+=====+ ... +=====+=====| /    240 or 288 lines after deinterlacing.
  *
  * Each group of FOUR bytes (RAi, RBi, Bi, Gi) where i=0..frame_width/2-1
  * defines ONE pixel. Therefore this format yields 176x144 "decoded"
  * resolution at best. I do not know why camera sends such format - the
- * previous model just used I420 and everyone was happy.
+ * previous model (1) just used interlaced I420 and everyone was happy.
  *
  * I do not know what is the difference between RAi and RBi bytes. Both
  * seemingly represent R component, but slightly vary in value (so that
@@ -934,29 +659,42 @@
  * them both as R component in attempt to at least partially recover the
  * lost resolution.
  */
-static scan_state_t usb_ibmcam_model2_parse_lines(struct usb_ibmcam *ibmcam, long *pcopylen)
+static ParseState_t ibmcam_model2_320x240_parse_lines(
+	uvd_t *uvd,
+	usbvideo_frame_t *frame,
+	long *pcopylen)
 {
-	struct ibmcam_frame *frame;
-	unsigned char *data, *f, *la, *lb;
+	unsigned char *f, *la, *lb;
 	unsigned int len;
-	const int v4l_linesize = imgwidth * V4L_BYTES_PER_PIXEL;	/* V4L line offset */
+	int v4l_linesize; /* V4L line offset */
 	int i, j, frame_done=0, color_corr;
+	int scanLength, scanHeight;
+	static unsigned char lineBuffer[352*2];
 
-	color_corr = (ibmcam->vpic.colour) >> 8; /* 0..+255 */
+	switch (uvd->videosize) {
+	case VIDEOSIZE_320x240:
+	case VIDEOSIZE_352x240:
+	case VIDEOSIZE_352x288:
+		scanLength = VIDEOSIZE_X(uvd->videosize);
+		scanHeight = VIDEOSIZE_Y(uvd->videosize);
+		break;
+	default:
+		err("ibmcam_model2_320x240_parse_lines: Wrong mode.");
+		return scan_Out;
+	}
 
-	data = ibmcam->scratch;
-	frame = &ibmcam->frame[ibmcam->curframe];
+	color_corr = (uvd->vpic.colour) >> 8; /* 0..+255 */
+	v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL;
 
-	/* Here we deal with pairs of horizontal lines */
-
-	len = frame->frmwidth * 2; /* 2 lines */
-	/*printk(KERN_DEBUG "len=%d. left=%d.\n",len,scratch_left(data));*/
+	len = scanLength * 2; /* See explanation above */
+	assert(len <= sizeof(lineBuffer));
 
 	/* Make sure there's enough data for the entire line */
-	if (scratch_left(data) < (len+32)) {
-		/*printk(KERN_DEBUG "out of data, need %u.\n", len);*/
+	if (RingQueue_GetLength(&uvd->dp) < len)
 		return scan_Out;
-	}
+
+	/* Suck one line out of the ring queue */
+	RingQueue_Dequeue(&uvd->dp, lineBuffer, len);
 
 	/*
 	 * Make sure that our writing into output buffer
@@ -964,19 +702,15 @@
 	 * not into current output scanline but in several after
 	 * it as well (if we enlarge image vertically.)
 	 */
-	if ((frame->curline + 1) >= V4L_FRAME_HEIGHT)
+	if ((frame->curline + 2) >= VIDEOSIZE_Y(frame->request))
 		return scan_NextFrame;
 
-	if ((frame->curline & 1) == 0) {
-		la = data;
-		lb = data + frame->frmwidth;
-	} else {
-		la = data + frame->frmwidth;
-		lb = data;
-	}
+	la = lineBuffer;
+	lb = lineBuffer + scanLength;
 
 	/*
-	 * Now we are sure that entire line (representing all 'frame->frmwidth'
+	 * Now we are sure that entire line (representing all
+	 *         VIDEOSIZE_X(frame->request)
 	 * pixels from the camera) is available in the scratch buffer. We
 	 * start copying the line left-aligned to the V4L buffer (which
 	 * might be larger - not smaller, hopefully). If the camera
@@ -986,14 +720,14 @@
 	f = frame->data + (v4l_linesize * frame->curline);
 
 	/* Fill the 2-line strip */
-	for (i = 0; i < frame->frmwidth; i++) {
+	for (i = 0; i < VIDEOSIZE_X(frame->request); i++) {
 		int y, rv, gv, bv;	/* RGB components */
 
 		j = i & (~1);
 
 		/* Check for various visual debugging hints (colorized pixels) */
-		if ((flags & FLAGS_DISPLAY_HINTS) && (ibmcam->has_hdr)) {
-			if (ibmcam->has_hdr == 1) {
+		if ((flags & FLAGS_DISPLAY_HINTS) && (IBMCAM_T(uvd)->has_hdr)) {
+			if (IBMCAM_T(uvd)->has_hdr == 1) {
 				bv = 0; /* Yellow marker */
 				gv = 0xFF;
 				rv = 0xFF;
@@ -1002,11 +736,27 @@
 				gv = 0xFF;
 				rv = 0;
 			}
-			ibmcam->has_hdr = 0;
+			IBMCAM_T(uvd)->has_hdr = 0;
 			goto make_pixel;
 		}
 
 		/*
+		 * Check if we are still in range. We may be out of range if our
+		 * V4L canvas is wider or taller than the camera "native" image.
+		 * Then we quickly fill the remainder of the line with zeros to
+		 * make black color and quit the horizontal scanning loop.
+		 */
+		if (((frame->curline + 2) >= scanHeight) || (i >= scanLength)) {
+			const int j = i * V4L_BYTES_PER_PIXEL;
+#if USES_IBMCAM_PUTPIXEL
+			/* Refresh 'f' because we don't use it much with PUTPIXEL */
+			f = frame->data + (v4l_linesize * frame->curline) + j;
+#endif
+			memset(f, 0, v4l_linesize - j);
+			break;
+		}
+
+		/*
 		 * Here I use RA and RB components, one per physical pixel.
 		 * This causes fine vertical grid on the picture but may improve
 		 * horizontal resolution. If you prefer replicating, use this:
@@ -1045,8 +795,7 @@
 		}
 
 	make_pixel:
-		IBMCAM_PUTPIXEL(frame, i, frame->curline, rv, gv, bv);
-		IBMCAM_PUTPIXEL(frame, i, frame->curline+1, rv, gv, bv);
+		RGB24_PUTPIXEL(frame, i, frame->curline, rv, gv, bv);
 	}
 	/*
 	 * Account for number of bytes that we wrote into output V4L frame.
@@ -1056,257 +805,342 @@
 	 */
 	frame->curline += 2;
 	*pcopylen += v4l_linesize * 2;
-	data += frame->frmwidth * 2;
-	usb_ibmcam_align_scratch(ibmcam, data);
+	frame->deinterlace = Deinterlace_FillOddLines;
 
-	if (frame_done || (frame->curline >= frame->frmheight))
+	if (frame_done || (frame->curline >= VIDEOSIZE_Y(frame->request)))
 		return scan_NextFrame;
 	else
 		return scan_Continue;
 }
 
-/*
- * ibmcam_parse_data()
- *
- * Generic routine to parse the scratch buffer. It employs either
- * usb_ibmcam_find_header() or usb_ibmcam_parse_lines() to do most
- * of work.
- *
- * History:
- * 1/21/00  Created.
- */
-static void ibmcam_parse_data(struct usb_ibmcam *ibmcam)
+static ParseState_t ibmcam_model3_parse_lines(
+	uvd_t *uvd,
+	usbvideo_frame_t *frame,
+	long *pcopylen)
 {
-	struct ibmcam_frame *frame;
-	unsigned char *data = ibmcam->scratch;
-	scan_state_t newstate;
-	long copylen = 0;
-
-	/* Grab the current frame and the previous frame */
-	frame = &ibmcam->frame[ibmcam->curframe];
+	unsigned char *data;
+	const unsigned char *color;
+	unsigned int len;
+	int v4l_linesize; /* V4L line offset */
+	const int hue_corr  = (uvd->vpic.hue - 0x8000) >> 10;	/* -32..+31 */
+	const int hue2_corr = (hue_correction - 128) / 4;		/* -32..+31 */
+	const int ccm = 128; /* Color correction median - see below */
+	int i, u, v, rw, data_w=0, data_h=0, color_corr;
+	static unsigned char lineBuffer[640*3];
 
-	/* printk(KERN_DEBUG "parsing %u.\n", ibmcam->scratchlen); */
+	color_corr = (uvd->vpic.colour - 0x8000) >> 8; /* -128..+127 = -ccm..+(ccm-1)*/
+	RESTRICT_TO_RANGE(color_corr, -ccm, ccm+1);
 
-	while (1) {
+	v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL;
 
-		newstate = scan_Out;
-		if (scratch_left(data)) {
-			if (frame->scanstate == STATE_SCANNING)
-				newstate = usb_ibmcam_find_header(ibmcam);
-			else if (frame->scanstate == STATE_LINES) {
-				if ((ibmcam->camera_model == IBMCAM_MODEL_2) &&
-				    (videosize >= VIDEOSIZE_352x288)) {
-					newstate = usb_ibmcam_model2_parse_lines(ibmcam, &copylen);
-				}
-				else {
-					newstate = usb_ibmcam_parse_lines(ibmcam, &copylen);
-				}
-			}
-		}
-		if (newstate == scan_Continue)
-			continue;
-		else if ((newstate == scan_NextFrame) || (newstate == scan_Out))
-			break;
-		else
-			return; /* scan_EndParse */
+	/* The header tells us what sort of data is in this frame */
+	switch (frame->header) {
+		/*
+		 * Uncompressed modes (that are easy to decode).
+		 */
+	case 0x0308:
+		data_w = 640;
+		data_h = 480;
+		break;
+	case 0x0208:
+		data_w = 320;
+		data_h = 240;
+		break;
+	case 0x020A:
+		data_w = 160;
+		data_h = 120;
+		break;
+		/*
+		 * Compressed modes (ViCE - that I don't know how to decode).
+		 */
+	case 0x0328:	/* 640x480, best quality compression */
+	case 0x0368:	/* 640x480, best frame rate compression */
+	case 0x0228:	/* 320x240, best quality compression */
+	case 0x0268:	/* 320x240, best frame rate compression */
+	case 0x02CA:	/* 160x120, best quality compression */
+	case 0x02EA:	/* 160x120, best frame rate compression */
+		/* Do nothing with this - not supported */
+		err("Unsupported mode $%04lx", frame->header);
+		return scan_NextFrame;
+	default:
+		/* Catch unknown headers, may help in learning new headers */
+		err("Strange frame->header=$%08lx", frame->header);
+		return scan_NextFrame;
 	}
 
-	if (newstate == scan_NextFrame) {
-		frame->grabstate = FRAME_DONE;
-		ibmcam->curframe = -1;
-		ibmcam->frame_num++;
-
-		/* Optionally display statistics on the screen */
-		if (flags & FLAGS_OVERLAY_STATS)
-			usb_ibmcam_overlaystats(ibmcam, frame);
-
-		/* This will cause the process to request another frame. */
-		if (waitqueue_active(&frame->wq))
-			wake_up_interruptible(&frame->wq);
+	/*
+	 * Make sure that our writing into output buffer
+	 * will not exceed the buffer. Note that we may write
+	 * not into current output scanline but in several after
+	 * it as well (if we enlarge image vertically.)
+	 */
+	if ((frame->curline + 1) >= data_h) {
+		if (uvd->debug >= 3)
+			info("Reached line %d. (frame is done)", frame->curline);
+		return scan_NextFrame;
 	}
 
-	/* Update the frame's uncompressed length. */
-	frame->scanlength += copylen;
-}
+	/* Make sure there's enough data for the entire line */
+	len = 3 * data_w; /* <y-data> <uv-data> */
+	assert(len <= sizeof(lineBuffer));
 
-/*
- * Make all of the blocks of data contiguous
- */
-static int ibmcam_compress_isochronous(struct usb_ibmcam *ibmcam, urb_t *urb)
-{
-	unsigned char *cdata, *data, *data0;
-	int i, totlen = 0;
+	/* Make sure there's enough data for the entire line */
+	if (RingQueue_GetLength(&uvd->dp) < len)
+		return scan_Out;
 
-	data = data0 = ibmcam->scratch + ibmcam->scratchlen;
-	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;
+	/* Suck one line out of the ring queue */
+	RingQueue_Dequeue(&uvd->dp, lineBuffer, len);
 
-		/* Detect and ignore errored packets */
-		if (st < 0) {
-			if (debug >= 1) {
-				printk(KERN_ERR "ibmcam data error: [%d] len=%d, status=%X\n",
-				       i, n, st);
-			}
-			ibmcam->iso_err_count++;
-			continue;
-		}
+	data = lineBuffer;
+	color = data + data_w;		/* Point to where color planes begin */
 
-		/* Detect and ignore empty packets */
-		if (n <= 0) {
-			ibmcam->iso_skip_count++;
-			continue;
-		}
+	/* Bottom-to-top scanning */
+	rw = (int)VIDEOSIZE_Y(frame->request) - (int)(frame->curline) - 1;
+	RESTRICT_TO_RANGE(rw, 0, VIDEOSIZE_Y(frame->request)-1);
 
-		/*
-		 * If camera continues to feed us with data but there is no
-		 * consumption (if, for example, V4L client fell asleep) we
-		 * may overflow the buffer. We have to move old data over to
-		 * free room for new data. This is bad for old data. If we
-		 * just drop new data then it's bad for new data... choose
-		 * your favorite evil here.
-		 */
-		if ((ibmcam->scratchlen + n) > scratchbufsize) {
-#if 0
-			ibmcam->scratch_ovf_count++;
-			if (debug >= 3)
-				printk(KERN_ERR "ibmcam: scratch buf overflow! "
-				       "scr_len: %d, n: %d\n", ibmcam->scratchlen, n );
-			return totlen;
-#else
-			int mv;
+	for (i = 0; i < VIDEOSIZE_X(frame->request); i++) {
+		int y, rv, gv, bv;	/* RGB components */
 
-			ibmcam->scratch_ovf_count++;
-			if (debug >= 3) {
-				printk(KERN_ERR "ibmcam: scratch buf overflow! "
-				       "scr_len: %d, n: %d\n", ibmcam->scratchlen, n );
-			}
-			mv  = (ibmcam->scratchlen + n) - scratchbufsize;
-			if (ibmcam->scratchlen >= mv) {
-				int newslen = ibmcam->scratchlen - mv;
-				memmove(ibmcam->scratch, ibmcam->scratch + mv, newslen);
-				ibmcam->scratchlen = newslen;
-				data = data0 = ibmcam->scratch + ibmcam->scratchlen;
-			} else {
-				printk(KERN_ERR "ibmcam: scratch buf too small\n");
-				return totlen;
+		if (i < data_w) {
+			y = data[i];	/* Luminosity is the first line */
+
+			/* Apply static color correction */
+			u = color[i*2] + hue_corr;
+			v = color[i*2 + 1] + hue2_corr;
+
+			/* Apply color correction */
+			if (color_corr != 0) {
+				/* Magnify up to 2 times, reduce down to zero saturation */
+				u = 128 + ((ccm + color_corr) * (u - 128)) / ccm;
+				v = 128 + ((ccm + color_corr) * (v - 128)) / ccm;
 			}
-#endif
-		}
+		} else
+			y = 0, u = v = 128;
 
-		/* Now we know that there is enough room in scratch buffer */
-		memmove(data, cdata, n);
-		data += n;
-		totlen += n;
-		ibmcam->scratchlen += n;
+		YUV_TO_RGB_BY_THE_BOOK(y, u, v, rv, gv, bv);
+		RGB24_PUTPIXEL(frame, i, rw, rv, gv, bv); /* Done by deinterlacing now */
 	}
-#if 0
-	if (totlen > 0) {
-		static int foo=0;
-		if (foo < 1) {
-			printk(KERN_DEBUG "+%d.\n", totlen);
-			ibmcam_hexdump(data0, (totlen > 64) ? 64:totlen);
-			++foo;
+	frame->deinterlace = Deinterlace_FillEvenLines;
+
+	/*
+	 * Account for number of bytes that we wrote into output V4L frame.
+	 * We do it here, after we are done with the scanline, because we
+	 * may fill more than one output scanline if we do vertical
+	 * enlargement.
+	 */
+	frame->curline += 2;
+	*pcopylen += 2 * v4l_linesize;
+
+	if (frame->curline >= VIDEOSIZE_Y(frame->request)) {
+		if (uvd->debug >= 3) {
+			info("All requested lines (%ld.) done.",
+			     VIDEOSIZE_Y(frame->request));
 		}
-	}
-#endif
-	return totlen;
+		return scan_NextFrame;
+	} else
+		return scan_Continue;
 }
 
-static void ibmcam_isoc_irq(struct urb *urb)
+/*
+ * ibmcam_model4_128x96_parse_lines()
+ *
+ * This decoder is for one strange data format that is produced by Model 4
+ * camera only in 128x96 mode. This is RGB format and here is its description.
+ * First of all, this is non-interlaced stream, meaning that all scan lines
+ * are present in the datastream. There are 96 consecutive blocks of data
+ * that describe all 96 lines of the image. Each block is 5*128 bytes long
+ * and carries R, G, B components. The format of the block is shown in the
+ * code below. First 128*2 bytes are interleaved R and G components. Then
+ * we have a gap (junk data) 64 bytes long. Then follow B and something
+ * else, also interleaved (this makes another 128*2 bytes). After that
+ * probably another 64 bytes of junk follow.
+ *
+ * History:
+ * 10-Feb-2001 Created.
+ */
+static ParseState_t ibmcam_model4_128x96_parse_lines(
+	uvd_t *uvd,
+	usbvideo_frame_t *frame,
+	long *pcopylen)
 {
-	int len;
-	struct usb_ibmcam *ibmcam = urb->context;
-	struct ibmcam_sbuf *sbuf;
-	int i;
+	const unsigned char *data_rv, *data_gv, *data_bv;
+	unsigned int len;
+	int i, v4l_linesize; /* V4L line offset */
+	const int data_w=128, data_h=96;
+	static unsigned char lineBuffer[128*5];
 
-	/* We don't want to do anything if we are about to be removed! */
-	if (!IBMCAM_IS_OPERATIONAL(ibmcam))
-		return;
+	v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL;
 
-#if 0
-	if (urb->actual_length > 0) {
-		printk(KERN_DEBUG "ibmcam_isoc_irq: %p status %d, "
-		       " errcount = %d, length = %d\n", urb, urb->status,
-		       urb->error_count, urb->actual_length);
-	} else {
-		static int c = 0;
-		if (c++ % 100 == 0)
-			printk(KERN_DEBUG "ibmcam_isoc_irq: no data\n");
+	/*
+	 * Make sure that our writing into output buffer
+	 * will not exceed the buffer. Note that we may write
+	 * not into current output scanline but in several after
+	 * it as well (if we enlarge image vertically.)
+	 */
+	if ((frame->curline + 1) >= data_h) {
+		if (uvd->debug >= 3)
+			info("Reached line %d. (frame is done)", frame->curline);
+		return scan_NextFrame;
 	}
-#endif
 
-	if (!ibmcam->streaming) {
-		if (debug >= 1)
-			printk(KERN_DEBUG "ibmcam: oops, not streaming, but interrupt\n");
-		return;
-	}
-	
-	sbuf = &ibmcam->sbuf[ibmcam->cursbuf];
+	/*
+	 * RGRGRG .... RGRG_____________B?B?B? ... B?B?____________
+	 * <---- 128*2 ---><---- 64 ---><--- 128*2 ---><--- 64 --->
+	 */
+
+	/* Make sure there's enough data for the entire line */
+	len = 5 * data_w;
+	assert(len <= sizeof(lineBuffer));
 
-	/* Copy the data received into our scratch buffer */
-	len = ibmcam_compress_isochronous(ibmcam, urb);
+	/* Make sure there's enough data for the entire line */
+	if (RingQueue_GetLength(&uvd->dp) < len)
+		return scan_Out;
 
-	ibmcam->urb_count++;
-	ibmcam->urb_length = len;
-	ibmcam->data_count += len;
-
-#if 0   /* This code prints few initial bytes of ISO data: used to decode markers */
-	if (ibmcam->urb_count % 64 == 1) {
-		if (ibmcam->urb_count == 1) {
-			ibmcam_hexdump(ibmcam->scratch,
-				       (ibmcam->scratchlen > 32) ? 32 : ibmcam->scratchlen);
+	/* Suck one line out of the ring queue */
+	RingQueue_Dequeue(&uvd->dp, lineBuffer, len);
+
+	data_rv = lineBuffer;
+	data_gv = lineBuffer + 1;
+	data_bv = lineBuffer + data_w*2 + data_w/2;
+	for (i = 0; i < VIDEOSIZE_X(frame->request); i++) {
+		int rv, gv, bv;	/* RGB components */
+		if (i < data_w) {
+			const int j = i * 2;
+			gv = data_rv[j];
+			rv = data_gv[j];
+			bv = data_bv[j];
+			if (flags & FLAGS_MONOCHROME) {
+				unsigned long y;
+				y = rv + gv + bv;
+				y /= 3;
+				if (y > 0xFF)
+					y = 0xFF;
+				rv = gv = bv = (unsigned char) y;
+			}
+		} else {
+			rv = gv = bv = 0;
 		}
+		RGB24_PUTPIXEL(frame, i, frame->curline, rv, gv, bv);
 	}
-#endif
+	frame->deinterlace = Deinterlace_None;
+	frame->curline++;
+	*pcopylen += v4l_linesize;
 
-	/* If we collected enough data let's parse! */
-	if (ibmcam->scratchlen) {
-		/* If we don't have a frame we're current working on, complain */
-		if (ibmcam->curframe >= 0)
-			ibmcam_parse_data(ibmcam);
-		else {
-			if (debug >= 1)
-				printk(KERN_DEBUG "ibmcam: received data, but no frame available\n");
+	if (frame->curline >= VIDEOSIZE_Y(frame->request)) {
+		if (uvd->debug >= 3) {
+			info("All requested lines (%ld.) done.",
+			     VIDEOSIZE_Y(frame->request));
+		}
+		return scan_NextFrame;
+	} else
+		return scan_Continue;
+}
+
+/*
+ * ibmcam_ProcessIsocData()
+ *
+ * Generic routine to parse the ring queue data. It employs either
+ * ibmcam_find_header() or ibmcam_parse_lines() to do most
+ * of work.
+ *
+ * History:
+ * 1/21/00  Created.
+ */
+void ibmcam_ProcessIsocData(uvd_t *uvd, usbvideo_frame_t *frame)
+{
+	ParseState_t newstate;
+	long copylen = 0;
+	int mod = IBMCAM_T(uvd)->camera_model;
+
+	while (1) {
+		newstate = scan_Out;
+		if (RingQueue_GetLength(&uvd->dp) > 0) {
+			if (frame->scanstate == ScanState_Scanning) {
+				newstate = ibmcam_find_header(uvd);
+			} else if (frame->scanstate == ScanState_Lines) {
+				if ((mod == IBMCAM_MODEL_2) &&
+				    ((uvd->videosize == VIDEOSIZE_352x288) ||
+				     (uvd->videosize == VIDEOSIZE_320x240) ||
+				     (uvd->videosize == VIDEOSIZE_352x240)))
+				{
+					newstate = ibmcam_model2_320x240_parse_lines(
+						uvd, frame, &copylen);
+				} else if (mod == IBMCAM_MODEL_4) {
+					/*
+					 * Model 4 cameras (IBM NetCamera) use Model 2 decoder (RGB)
+					 * for 320x240 and above; 160x120 and 176x144 uses Model 1
+					 * decoder (YUV), and 128x96 mode uses ???
+					 */
+					if ((uvd->videosize == VIDEOSIZE_352x288) ||
+					    (uvd->videosize == VIDEOSIZE_320x240) ||
+					    (uvd->videosize == VIDEOSIZE_352x240))
+					{
+						newstate = ibmcam_model2_320x240_parse_lines(uvd, frame, &copylen);
+					} else if (uvd->videosize == VIDEOSIZE_128x96) {
+						newstate = ibmcam_model4_128x96_parse_lines(uvd, frame, &copylen);
+					} else {
+						newstate = ibmcam_parse_lines(uvd, frame, &copylen);
+					}
+				} else if (mod == IBMCAM_MODEL_3) {
+					newstate = ibmcam_model3_parse_lines(uvd, frame, &copylen);
+				} else {
+					newstate = ibmcam_parse_lines(uvd, frame, &copylen);
+				}
+			}
 		}
+		if (newstate == scan_Continue)
+			continue;
+		else if ((newstate == scan_NextFrame) || (newstate == scan_Out))
+			break;
+		else
+			return; /* scan_EndParse */
 	}
 
-	for (i = 0; i < FRAMES_PER_DESC; i++) {
-		sbuf->urb->iso_frame_desc[i].status = 0;
-		sbuf->urb->iso_frame_desc[i].actual_length = 0;
+	if (newstate == scan_NextFrame) {
+		frame->frameState = FrameState_Done;
+		uvd->curframe = -1;
+		uvd->stats.frame_num++;
+		if ((mod == IBMCAM_MODEL_2) || (mod == IBMCAM_MODEL_4)) {
+			/* Need software contrast adjustment for those cameras */
+			frame->flags |= USBVIDEO_FRAME_FLAG_SOFTWARE_CONTRAST;
+		}
 	}
 
-	/* Move to the next sbuf */
-	ibmcam->cursbuf = (ibmcam->cursbuf + 1) % IBMCAM_NUMSBUF;
+	/* Update the frame's uncompressed length. */
+	frame->seqRead_Length += copylen;
 
-	return;
+#if 0
+	{
+		static unsigned char j=0;
+		memset(frame->data, j++, uvd->max_frame_size);
+		frame->frameState = FrameState_Ready;
+	}
+#endif
 }
 
 /*
- * usb_ibmcam_veio()
+ * ibmcam_veio()
  *
  * History:
  * 1/27/00  Added check for dev == NULL; this happens if camera is unplugged.
  */
-static int usb_ibmcam_veio(
-	struct usb_ibmcam *ibmcam,
+static int ibmcam_veio(
+	uvd_t *uvd,
 	unsigned char req,
 	unsigned short value,
 	unsigned short index)
 {
-	static const char proc[] = "usb_ibmcam_veio";
+	static const char proc[] = "ibmcam_veio";
 	unsigned char cp[8] /* = { 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef } */;
 	int i;
 
-	if (!IBMCAM_IS_OPERATIONAL(ibmcam))
+	if (!CAMERA_IS_OPERATIONAL(uvd))
 		return 0;
 
 	if (req == 1) {
 		i = usb_control_msg(
-			ibmcam->dev,
-			usb_rcvctrlpipe(ibmcam->dev, 0),
+			uvd->dev,
+			usb_rcvctrlpipe(uvd->dev, 0),
 			req,
 			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
 			value,
@@ -1315,15 +1149,15 @@
 			sizeof(cp),
 			HZ);
 #if 0
-		printk(KERN_DEBUG "USB => %02x%02x%02x%02x%02x%02x%02x%02x "
-		       "(req=$%02x val=$%04x ind=$%04x)\n",
+		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(
-			ibmcam->dev,
-			usb_sndctrlpipe(ibmcam->dev, 0),
+			uvd->dev,
+			usb_sndctrlpipe(uvd->dev, 0),
 			req,
 			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
 			value,
@@ -1333,15 +1167,15 @@
 			HZ);
 	}
 	if (i < 0) {
-		printk(KERN_ERR "%s: ERROR=%d. Camera stopped - "
-		       "reconnect or reload driver.\n", proc, i);
-		ibmcam->last_error = i;
+		err("%s: ERROR=%d. Camera stopped; Reconnect or reload driver.",
+		    proc, i);
+		uvd->last_error = i;
 	}
 	return i;
 }
 
 /*
- * usb_ibmcam_calculate_fps()
+ * ibmcam_calculate_fps()
  *
  * This procedure roughly calculates the real frame rate based
  * on FPS code (framerate=NNN option). Actual FPS differs
@@ -1358,13 +1192,13 @@
  * History:
  * 1/18/00  Created.
  */
-static int usb_ibmcam_calculate_fps(void)
+static int ibmcam_calculate_fps(uvd_t *uvd)
 {
 	return 3 + framerate*4 + framerate/2;
 }
 
 /*
- * usb_ibmcam_send_FF_04_02()
+ * ibmcam_send_FF_04_02()
  *
  * This procedure sends magic 3-command prefix to the camera.
  * The purpose of this prefix is not known.
@@ -1372,108 +1206,140 @@
  * History:
  * 1/2/00   Created.
  */
-static void usb_ibmcam_send_FF_04_02(struct usb_ibmcam *ibmcam)
+static void ibmcam_send_FF_04_02(uvd_t *uvd)
 {
-	usb_ibmcam_veio(ibmcam, 0, 0x00FF, 0x0127);
-	usb_ibmcam_veio(ibmcam, 0, 0x0004, 0x0124);
-	usb_ibmcam_veio(ibmcam, 0, 0x0002, 0x0124);
+	ibmcam_veio(uvd, 0, 0x00FF, 0x0127);
+	ibmcam_veio(uvd, 0, 0x0004, 0x0124);
+	ibmcam_veio(uvd, 0, 0x0002, 0x0124);
 }
 
-static void usb_ibmcam_send_00_04_06(struct usb_ibmcam *ibmcam)
+static void ibmcam_send_00_04_06(uvd_t *uvd)
 {
-	usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0127);
-	usb_ibmcam_veio(ibmcam, 0, 0x0004, 0x0124);
-	usb_ibmcam_veio(ibmcam, 0, 0x0006, 0x0124);
+	ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+	ibmcam_veio(uvd, 0, 0x0004, 0x0124);
+	ibmcam_veio(uvd, 0, 0x0006, 0x0124);
 }
 
-static void usb_ibmcam_send_x_00(struct usb_ibmcam *ibmcam, unsigned short x)
+static void ibmcam_send_x_00(uvd_t *uvd, unsigned short x)
 {
-	usb_ibmcam_veio(ibmcam, 0, x,      0x0127);
-	usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0124);
+	ibmcam_veio(uvd, 0, x,      0x0127);
+	ibmcam_veio(uvd, 0, 0x0000, 0x0124);
 }
 
-static void usb_ibmcam_send_x_00_05(struct usb_ibmcam *ibmcam, unsigned short x)
+static void ibmcam_send_x_00_05(uvd_t *uvd, unsigned short x)
 {
-	usb_ibmcam_send_x_00(ibmcam, x);
-	usb_ibmcam_veio(ibmcam, 0, 0x0005, 0x0124);
+	ibmcam_send_x_00(uvd, x);
+	ibmcam_veio(uvd, 0, 0x0005, 0x0124);
 }
 
-static void usb_ibmcam_send_x_00_05_02(struct usb_ibmcam *ibmcam, unsigned short x)
+static void ibmcam_send_x_00_05_02(uvd_t *uvd, unsigned short x)
 {
-	usb_ibmcam_veio(ibmcam, 0, x,      0x0127);
-	usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0124);
-	usb_ibmcam_veio(ibmcam, 0, 0x0005, 0x0124);
-	usb_ibmcam_veio(ibmcam, 0, 0x0002, 0x0124);
+	ibmcam_veio(uvd, 0, x,      0x0127);
+	ibmcam_veio(uvd, 0, 0x0000, 0x0124);
+	ibmcam_veio(uvd, 0, 0x0005, 0x0124);
+	ibmcam_veio(uvd, 0, 0x0002, 0x0124);
 }
 
-static void usb_ibmcam_send_x_01_00_05(struct usb_ibmcam *ibmcam, unsigned short x)
+static void ibmcam_send_x_01_00_05(uvd_t *uvd, unsigned short x)
 {
-	usb_ibmcam_veio(ibmcam, 0, x,      0x0127);
-	usb_ibmcam_veio(ibmcam, 0, 0x0001, 0x0124);
-	usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0124);
-	usb_ibmcam_veio(ibmcam, 0, 0x0005, 0x0124);
+	ibmcam_veio(uvd, 0, x,      0x0127);
+	ibmcam_veio(uvd, 0, 0x0001, 0x0124);
+	ibmcam_veio(uvd, 0, 0x0000, 0x0124);
+	ibmcam_veio(uvd, 0, 0x0005, 0x0124);
 }
 
-static void usb_ibmcam_send_x_00_05_02_01(struct usb_ibmcam *ibmcam, unsigned short x)
+static void ibmcam_send_x_00_05_02_01(uvd_t *uvd, unsigned short x)
 {
-	usb_ibmcam_veio(ibmcam, 0, x,      0x0127);
-	usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0124);
-	usb_ibmcam_veio(ibmcam, 0, 0x0005, 0x0124);
-	usb_ibmcam_veio(ibmcam, 0, 0x0002, 0x0124);
-	usb_ibmcam_veio(ibmcam, 0, 0x0001, 0x0124);
+	ibmcam_veio(uvd, 0, x,      0x0127);
+	ibmcam_veio(uvd, 0, 0x0000, 0x0124);
+	ibmcam_veio(uvd, 0, 0x0005, 0x0124);
+	ibmcam_veio(uvd, 0, 0x0002, 0x0124);
+	ibmcam_veio(uvd, 0, 0x0001, 0x0124);
 }
 
-static void usb_ibmcam_send_x_00_05_02_08_01(struct usb_ibmcam *ibmcam, unsigned short x)
+static void ibmcam_send_x_00_05_02_08_01(uvd_t *uvd, unsigned short x)
 {
-	usb_ibmcam_veio(ibmcam, 0, x,      0x0127);
-	usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0124);
-	usb_ibmcam_veio(ibmcam, 0, 0x0005, 0x0124);
-	usb_ibmcam_veio(ibmcam, 0, 0x0002, 0x0124);
-	usb_ibmcam_veio(ibmcam, 0, 0x0008, 0x0124);
-	usb_ibmcam_veio(ibmcam, 0, 0x0001, 0x0124);
+	ibmcam_veio(uvd, 0, x,      0x0127);
+	ibmcam_veio(uvd, 0, 0x0000, 0x0124);
+	ibmcam_veio(uvd, 0, 0x0005, 0x0124);
+	ibmcam_veio(uvd, 0, 0x0002, 0x0124);
+	ibmcam_veio(uvd, 0, 0x0008, 0x0124);
+	ibmcam_veio(uvd, 0, 0x0001, 0x0124);
 }
 
-static void usb_ibmcam_Packet_Format1(struct usb_ibmcam *ibmcam, unsigned char fkey, unsigned char val)
+static void ibmcam_Packet_Format1(uvd_t *uvd, unsigned char fkey, unsigned char val)
 {
-	usb_ibmcam_send_x_01_00_05	(ibmcam, unknown_88);
-	usb_ibmcam_send_x_00_05		(ibmcam, fkey);
-	usb_ibmcam_send_x_00_05_02_08_01(ibmcam, val);
-	usb_ibmcam_send_x_00_05		(ibmcam, unknown_88);
-	usb_ibmcam_send_x_00_05_02_01	(ibmcam, fkey);
-	usb_ibmcam_send_x_00_05		(ibmcam, unknown_89);
-	usb_ibmcam_send_x_00		(ibmcam, fkey);
-	usb_ibmcam_send_00_04_06	(ibmcam);
-	usb_ibmcam_veio			(ibmcam, 1, 0x0000, 0x0126);
-	usb_ibmcam_send_FF_04_02	(ibmcam);
+	ibmcam_send_x_01_00_05(uvd, unknown_88);
+	ibmcam_send_x_00_05(uvd, fkey);
+	ibmcam_send_x_00_05_02_08_01(uvd, val);
+	ibmcam_send_x_00_05(uvd, unknown_88);
+	ibmcam_send_x_00_05_02_01(uvd, fkey);
+	ibmcam_send_x_00_05(uvd, unknown_89);
+	ibmcam_send_x_00(uvd, fkey);
+	ibmcam_send_00_04_06(uvd);
+	ibmcam_veio(uvd, 1, 0x0000, 0x0126);
+	ibmcam_send_FF_04_02(uvd);
 }
 
-static void usb_ibmcam_PacketFormat2(struct usb_ibmcam *ibmcam, unsigned char fkey, unsigned char val)
+static void ibmcam_PacketFormat2(uvd_t *uvd, unsigned char fkey, unsigned char val)
 {
-	usb_ibmcam_send_x_01_00_05	(ibmcam, unknown_88);
-	usb_ibmcam_send_x_00_05		(ibmcam, fkey);
-	usb_ibmcam_send_x_00_05_02	(ibmcam, val);
+	ibmcam_send_x_01_00_05	(uvd, unknown_88);
+	ibmcam_send_x_00_05	(uvd, fkey);
+	ibmcam_send_x_00_05_02	(uvd, val);
 }
 
-static void usb_ibmcam_model2_Packet2(struct usb_ibmcam *ibmcam)
+static void ibmcam_model2_Packet2(uvd_t *uvd)
 {
-	usb_ibmcam_veio(ibmcam, 0, 0x00ff, 0x012d);
-	usb_ibmcam_veio(ibmcam, 0, 0xfea3, 0x0124);
+	ibmcam_veio(uvd, 0, 0x00ff, 0x012d);
+	ibmcam_veio(uvd, 0, 0xfea3, 0x0124);
 }
 
-static void usb_ibmcam_model2_Packet1(struct usb_ibmcam *ibmcam, unsigned short v1, unsigned short v2)
+static void ibmcam_model2_Packet1(uvd_t *uvd, unsigned short v1, unsigned short v2)
 {
-	usb_ibmcam_veio(ibmcam, 0, 0x00aa, 0x012d);
-	usb_ibmcam_veio(ibmcam, 0, 0x00ff, 0x012e);
-	usb_ibmcam_veio(ibmcam, 0, v1, 	   0x012f);
-	usb_ibmcam_veio(ibmcam, 0, 0x00ff, 0x0130);
-	usb_ibmcam_veio(ibmcam, 0, 0xc719, 0x0124);
-	usb_ibmcam_veio(ibmcam, 0, v2,     0x0127);
+	ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+	ibmcam_veio(uvd, 0, 0x00ff, 0x012e);
+	ibmcam_veio(uvd, 0, v1,     0x012f);
+	ibmcam_veio(uvd, 0, 0x00ff, 0x0130);
+	ibmcam_veio(uvd, 0, 0xc719, 0x0124);
+	ibmcam_veio(uvd, 0, v2,     0x0127);
 
-	usb_ibmcam_model2_Packet2(ibmcam);
+	ibmcam_model2_Packet2(uvd);
 }
 
 /*
- * usb_ibmcam_adjust_contrast()
+ * ibmcam_model3_Packet1()
+ *
+ * 00_0078_012d	
+ * 00_0097_012f
+ * 00_d141_0124	
+ * 00_0096_0127
+ * 00_fea8_0124	
+*/
+static void ibmcam_model3_Packet1(uvd_t *uvd, unsigned short v1, unsigned short v2)
+{
+	ibmcam_veio(uvd, 0, 0x0078, 0x012d);
+	ibmcam_veio(uvd, 0, v1,     0x012f);
+	ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+	ibmcam_veio(uvd, 0, v2,     0x0127);
+	ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+}
+
+static void ibmcam_model4_BrightnessPacket(uvd_t *uvd, int i)
+{
+	ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+	ibmcam_veio(uvd, 0, 0x0026, 0x012f);
+	ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+	ibmcam_veio(uvd, 0, i,      0x0127);
+	ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+	ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+	ibmcam_veio(uvd, 0, 0x0038, 0x012d);
+	ibmcam_veio(uvd, 0, 0x0004, 0x012f);
+	ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+	ibmcam_veio(uvd, 0, 0xfffa, 0x0124);
+}
+
+/*
+ * ibmcam_adjust_contrast()
  *
  * The contrast value changes from 0 (high contrast) to 15 (low contrast).
  * This is in reverse to usual order of things (such as TV controls), so
@@ -1484,30 +1350,65 @@
  * History:
  * 1/2/00   Created.
  */
-static void usb_ibmcam_adjust_contrast(struct usb_ibmcam *ibmcam)
+static void ibmcam_adjust_contrast(uvd_t *uvd)
 {
-	unsigned char new_contrast = ibmcam->vpic.contrast >> 12;
-	const int ntries = 5;
+	unsigned char a_contrast = uvd->vpic.contrast >> 12;
+	unsigned char new_contrast;
 
-	if (new_contrast >= 16)
-		new_contrast = 15;
-	new_contrast = 15 - new_contrast;
-	if (new_contrast != ibmcam->vpic_old.contrast) {
-		ibmcam->vpic_old.contrast = new_contrast;
-		if (ibmcam->camera_model == IBMCAM_MODEL_1) {
-			int i;
-			for (i=0; i < ntries; i++) {
-				usb_ibmcam_Packet_Format1(ibmcam, contrast_14, new_contrast);
-				usb_ibmcam_send_FF_04_02(ibmcam);
-			}
-		} else {
-			/* Camera model 2 does not have this control; implemented in software. */
+	if (a_contrast >= 16)
+		a_contrast = 15;
+	new_contrast = 15 - a_contrast;
+	if (new_contrast == uvd->vpic_old.contrast)
+		return;
+	uvd->vpic_old.contrast = new_contrast;
+	switch (IBMCAM_T(uvd)->camera_model) {
+	case IBMCAM_MODEL_1:
+	{
+		const int ntries = 5;
+		int i;
+		for (i=0; i < ntries; i++) {
+			ibmcam_Packet_Format1(uvd, contrast_14, new_contrast);
+			ibmcam_send_FF_04_02(uvd);
 		}
+		break;
+	}
+	case IBMCAM_MODEL_2:
+	case IBMCAM_MODEL_4:
+		/* Models 2, 4 do not have this control; implemented in software. */
+		break;
+	case IBMCAM_MODEL_3:
+	{	/* Preset hardware values */
+		static const struct {
+			unsigned short cv1;
+			unsigned short cv2;
+			unsigned short cv3;
+		} cv[7] = {
+			{ 0x05, 0x05, 0x0f },	/* Minimum */
+			{ 0x04, 0x04, 0x16 },
+			{ 0x02, 0x03, 0x16 },
+			{ 0x02, 0x08, 0x16 },
+			{ 0x01, 0x0c, 0x16 },
+			{ 0x01, 0x0e, 0x16 },
+			{ 0x01, 0x10, 0x16 }	/* Maximum */
+		};
+		int i = a_contrast / 2;
+		RESTRICT_TO_RANGE(i, 0, 6);
+		ibmcam_veio(uvd, 0, 0x0000, 0x010c);	/* Stop */
+		ibmcam_model3_Packet1(uvd, 0x0067, cv[i].cv1);
+		ibmcam_model3_Packet1(uvd, 0x005b, cv[i].cv2);
+		ibmcam_model3_Packet1(uvd, 0x005c, cv[i].cv3);
+		ibmcam_veio(uvd, 0, 0x0001, 0x0114);
+		ibmcam_veio(uvd, 0, 0x00c0, 0x010c);	/* Go! */
+		usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp));
+		break;
+	}
+	default:
+		break;
 	}
 }
 
 /*
- * usb_ibmcam_change_lighting_conditions()
+ * ibmcam_change_lighting_conditions()
  *
  * Camera model 1:
  * We have 3 levels of lighting conditions: 0=Bright, 1=Medium, 2=Low.
@@ -1524,19 +1425,24 @@
  * 1/5/00   Created.
  * 2/20/00  Added support for Model 2 cameras.
  */
-static void usb_ibmcam_change_lighting_conditions(struct usb_ibmcam *ibmcam)
+static void ibmcam_change_lighting_conditions(uvd_t *uvd)
 {
-	static const char proc[] = "usb_ibmcam_change_lighting_conditions";
+	static const char proc[] = "ibmcam_change_lighting_conditions";
 
 	if (debug > 0)
-		printk(KERN_INFO "%s: Set lighting to %hu.\n", proc, lighting);
+		info("%s: Set lighting to %hu.", proc, lighting);
 
-	if (ibmcam->camera_model == IBMCAM_MODEL_1) {
+	switch (IBMCAM_T(uvd)->camera_model) {
+	case IBMCAM_MODEL_1:
+	{
 		const int ntries = 5;
 		int i;
 		for (i=0; i < ntries; i++)
-			usb_ibmcam_Packet_Format1(ibmcam, light_27, (unsigned short) lighting);
-	} else {
+			ibmcam_Packet_Format1(uvd, light_27, (unsigned short) lighting);
+		break;
+	}
+	case IBMCAM_MODEL_2:
+#if 0
 		/*
 		 * This command apparently requires camera to be stopped. My
 		 * experiments showed that it -is- possible to alter the lighting
@@ -1546,385 +1452,549 @@
 		 * is commented out because it does not work at -any- moment, so its
 		 * presence makes no sense. You may use it for experiments.
 		 */
-#if 0
-		usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x010c);	/* Stop camera */
-		usb_ibmcam_model2_Packet1(ibmcam, mod2_sensitivity, lighting);
-		usb_ibmcam_veio(ibmcam, 0, 0x00c0, 0x010c);	/* Start camera */
+		ibmcam_veio(uvd, 0, 0x0000, 0x010c);	/* Stop camera */
+		ibmcam_model2_Packet1(uvd, mod2_sensitivity, lighting);
+		ibmcam_veio(uvd, 0, 0x00c0, 0x010c);	/* Start camera */
 #endif
+		break;
+	case IBMCAM_MODEL_3:
+	case IBMCAM_MODEL_4:
+	default:
+		break;
 	}
 }
 
 /*
- * usb_ibmcam_set_sharpness()
+ * ibmcam_set_sharpness()
  *
  * Cameras model 1 have internal smoothing feature. It is controlled by value in
  * range [0..6], where 0 is most smooth and 6 is most sharp (raw image, I guess).
  * Recommended value is 4. Cameras model 2 do not have this feature at all.
  */
-static void usb_ibmcam_set_sharpness(struct usb_ibmcam *ibmcam)
+static void ibmcam_set_sharpness(uvd_t *uvd)
 {
-	static const char proc[] = "usb_ibmcam_set_sharpness";
+	static const char proc[] = "ibmcam_set_sharpness";
 
-	if (ibmcam->camera_model == IBMCAM_MODEL_1) {
+	switch (IBMCAM_T(uvd)->camera_model) {
+	case IBMCAM_MODEL_1:
+	{
 		static const unsigned short sa[] = { 0x11, 0x13, 0x16, 0x18, 0x1a, 0x8, 0x0a };
 		unsigned short i, sv;
 
 		RESTRICT_TO_RANGE(sharpness, SHARPNESS_MIN, SHARPNESS_MAX);
 		if (debug > 0)
-			printk(KERN_INFO "%s: Set sharpness to %hu.\n", proc, sharpness);
+			info("%s: Set sharpness to %hu.", proc, sharpness);
 
 		sv = sa[sharpness - SHARPNESS_MIN];
 		for (i=0; i < 2; i++) {
-			usb_ibmcam_send_x_01_00_05	(ibmcam, unknown_88);
-			usb_ibmcam_send_x_00_05		(ibmcam, sharp_13);
-			usb_ibmcam_send_x_00_05_02	(ibmcam, sv);
+			ibmcam_send_x_01_00_05	(uvd, unknown_88);
+			ibmcam_send_x_00_05		(uvd, sharp_13);
+			ibmcam_send_x_00_05_02	(uvd, sv);
 		}
-	} else {
-		/* Camera model 2 does not have this control */
+		break;
+	}
+	case IBMCAM_MODEL_2:
+	case IBMCAM_MODEL_4:
+		/* Models 2, 4 do not have this control */
+		break;
+	case IBMCAM_MODEL_3:
+	{	/*
+		 * "Use a table of magic numbers.
+		 *  This setting doesn't really change much.
+		 *  But that's how Windows does it."
+		 */
+		static const struct {
+			unsigned short sv1;
+			unsigned short sv2;
+			unsigned short sv3;
+			unsigned short sv4;
+		} sv[7] = {
+			{ 0x00, 0x00, 0x05, 0x14 },	/* Smoothest */
+			{ 0x01, 0x04, 0x05, 0x14 },
+			{ 0x02, 0x04, 0x05, 0x14 },
+			{ 0x03, 0x04, 0x05, 0x14 },
+			{ 0x03, 0x05, 0x05, 0x14 },
+			{ 0x03, 0x06, 0x05, 0x14 },
+			{ 0x03, 0x07, 0x05, 0x14 }	/* Sharpest */
+		};
+		RESTRICT_TO_RANGE(sharpness, SHARPNESS_MIN, SHARPNESS_MAX);
+		RESTRICT_TO_RANGE(sharpness, 0, 6);
+		ibmcam_veio(uvd, 0, 0x0000, 0x010c);	/* Stop */
+		ibmcam_model3_Packet1(uvd, 0x0060, sv[sharpness].sv1);
+		ibmcam_model3_Packet1(uvd, 0x0061, sv[sharpness].sv2);
+		ibmcam_model3_Packet1(uvd, 0x0062, sv[sharpness].sv3);
+		ibmcam_model3_Packet1(uvd, 0x0063, sv[sharpness].sv4);
+		ibmcam_veio(uvd, 0, 0x0001, 0x0114);
+		ibmcam_veio(uvd, 0, 0x00c0, 0x010c);	/* Go! */
+		usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp));
+		ibmcam_veio(uvd, 0, 0x0001, 0x0113);
+		break;
+	}
+	default:
+		break;
 	}
 }
 
 /*
- * usb_ibmcam_set_brightness()
+ * ibmcam_set_brightness()
  *
  * This procedure changes brightness of the picture.
  */
-static void usb_ibmcam_set_brightness(struct usb_ibmcam *ibmcam)
+static void ibmcam_set_brightness(uvd_t *uvd)
 {
-	static const char proc[] = "usb_ibmcam_set_brightness";
+	static const char proc[] = "ibmcam_set_brightness";
 	static const unsigned short n = 1;
-	unsigned short i, j, bv[3];
-
-	bv[0] = bv[1] = bv[2] = ibmcam->vpic.brightness >> 10;
-	if (bv[0] == (ibmcam->vpic_old.brightness >> 10))
-		return;
-	ibmcam->vpic_old.brightness = ibmcam->vpic.brightness;
 
 	if (debug > 0)
-		printk(KERN_INFO "%s: Set brightness to (%hu,%hu,%hu)\n",
-		       proc, bv[0], bv[1], bv[2]);
+		info("%s: Set brightness to %hu.", proc, uvd->vpic.brightness);
 
-	if (ibmcam->camera_model == IBMCAM_MODEL_1) {
+	switch (IBMCAM_T(uvd)->camera_model) {
+	case IBMCAM_MODEL_1:
+	{
+		unsigned short i, j, bv[3];
+		bv[0] = bv[1] = bv[2] = uvd->vpic.brightness >> 10;
+		if (bv[0] == (uvd->vpic_old.brightness >> 10))
+			return;
+		uvd->vpic_old.brightness = bv[0];
 		for (j=0; j < 3; j++)
 			for (i=0; i < n; i++)
-				usb_ibmcam_Packet_Format1(ibmcam, bright_3x[j], bv[j]);
-	} else {
-		i = ibmcam->vpic.brightness >> 12;	/* 0 .. 15 */
+				ibmcam_Packet_Format1(uvd, bright_3x[j], bv[j]);
+		break;
+	}
+	case IBMCAM_MODEL_2:
+	{
+		unsigned short i, j;
+		i = uvd->vpic.brightness >> 12;	/* 0 .. 15 */
 		j = 0x60 + i * ((0xee - 0x60) / 16);	/* 0x60 .. 0xee or so */
-		usb_ibmcam_model2_Packet1(ibmcam, mod2_brightness, j);
+		if (uvd->vpic_old.brightness == j)
+			break;
+		uvd->vpic_old.brightness = j;
+		ibmcam_model2_Packet1(uvd, mod2_brightness, j);
+		break;
+	}
+	case IBMCAM_MODEL_3:
+	{
+		/* Model 3: Brightness range 'i' in [0x0C..0x3F] */
+		unsigned short i =
+			0x0C + (uvd->vpic.brightness / (0xFFFF / (0x3F - 0x0C + 1)));
+		RESTRICT_TO_RANGE(i, 0x0C, 0x3F);
+		if (uvd->vpic_old.brightness == i)
+			break;
+		uvd->vpic_old.brightness = i;
+		ibmcam_veio(uvd, 0, 0x0000, 0x010c);	/* Stop */
+		ibmcam_model3_Packet1(uvd, 0x0036, i);
+		ibmcam_veio(uvd, 0, 0x0001, 0x0114);
+		ibmcam_veio(uvd, 0, 0x00c0, 0x010c);	/* Go! */
+		usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp));
+		ibmcam_veio(uvd, 0, 0x0001, 0x0113);
+		break;
+	}
+	case IBMCAM_MODEL_4:
+	{
+		/* Model 4: Brightness range 'i' in [0x04..0xb4] */
+		unsigned short i = 0x04 + (uvd->vpic.brightness / (0xFFFF / (0xb4 - 0x04 + 1)));
+		RESTRICT_TO_RANGE(i, 0x04, 0xb4);
+		if (uvd->vpic_old.brightness == i)
+			break;
+		uvd->vpic_old.brightness = i;
+		ibmcam_model4_BrightnessPacket(uvd, i);
+		break;
+	}
+	default:
+		break;
 	}
 }
 
-static void usb_ibmcam_model2_set_hue(struct usb_ibmcam *ibmcam)
+static void ibmcam_set_hue(uvd_t *uvd)
 {
-	unsigned short hue = ibmcam->vpic.hue >> 9; /* 0 .. 7F */
+	switch (IBMCAM_T(uvd)->camera_model) {
+	case IBMCAM_MODEL_2:
+	{
+		unsigned short hue = uvd->vpic.hue >> 9; /* 0 .. 7F */
+		if (uvd->vpic_old.hue == hue)
+			return;
+		uvd->vpic_old.hue = hue;
+		ibmcam_model2_Packet1(uvd, mod2_hue, hue);
+		/* ibmcam_model2_Packet1(uvd, mod2_saturation, sat); */
+		break;
+	}
+	case IBMCAM_MODEL_3:
+	{
+#if 0 /* This seems not to work. No problem, will fix programmatically */
+		unsigned short hue = 0x05 + (uvd->vpic.hue / (0xFFFF / (0x37 - 0x05 + 1)));
+		RESTRICT_TO_RANGE(hue, 0x05, 0x37);
+		if (uvd->vpic_old.hue == hue)
+			return;
+		uvd->vpic_old.hue = hue;
+		ibmcam_veio(uvd, 0, 0x0000, 0x010c);	/* Stop */
+		ibmcam_model3_Packet1(uvd, 0x007e, hue);
+		ibmcam_veio(uvd, 0, 0x0001, 0x0114);
+		ibmcam_veio(uvd, 0, 0x00c0, 0x010c);	/* Go! */
+		usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp));
+		ibmcam_veio(uvd, 0, 0x0001, 0x0113);
+#endif
+		break;
+	}
+	case IBMCAM_MODEL_4:
+	{
+		unsigned short r_gain, g_gain, b_gain, hue;
+
+		/*
+		 * I am not sure r/g/b_gain variables exactly control gain
+		 * of those channels. Most likely they subtly change some
+		 * very internal image processing settings in the camera.
+		 * In any case, here is what they do, and feel free to tweak:
+		 *
+		 * r_gain: seriously affects red gain
+		 * g_gain: seriously affects green gain
+		 * b_gain: seriously affects blue gain
+		 * hue: changes average color from violet (0) to red (0xFF)
+		 *
+		 * These settings are preset for a decent white balance in
+		 * 320x240, 352x288 modes. Low-res modes exhibit higher contrast
+		 * and therefore may need different values here.
+		 */
+		hue = 20 + (uvd->vpic.hue >> 9);
+		switch (uvd->videosize) {
+		case VIDEOSIZE_128x96:
+			r_gain = 90;
+			g_gain = 166;
+			b_gain = 175;
+			break;
+		case VIDEOSIZE_160x120:
+			r_gain = 70;
+			g_gain = 166;
+			b_gain = 185;
+			break;
+		case VIDEOSIZE_176x144:
+			r_gain = 160;
+			g_gain = 175;
+			b_gain = 185;
+			break;
+		default:
+			r_gain = 120;
+			g_gain = 166;
+			b_gain = 175;
+			break;
+		}
+		RESTRICT_TO_RANGE(hue, 1, 0x7f);
 
-	usb_ibmcam_model2_Packet1(ibmcam, mod2_color_balance_rg, hue);
-	/* usb_ibmcam_model2_Packet1(ibmcam, mod2_saturation, sat); */
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x001e, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, g_gain, 0x0127);	/* Green gain */
+		ibmcam_veio(uvd, 0, r_gain, 0x012e);	/* Red gain */
+		ibmcam_veio(uvd, 0, b_gain, 0x0130);	/* Blue gain */
+		ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+		ibmcam_veio(uvd, 0, hue,    0x012d);	/* Hue */
+		ibmcam_veio(uvd, 0, 0xf545, 0x0124);
+		break;
+	}
+	default:
+		break;
+	}
 }
 
 /*
- * usb_ibmcam_adjust_picture()
+ * ibmcam_adjust_picture()
  *
  * This procedure gets called from V4L interface to update picture settings.
  * Here we change brightness and contrast.
  */
-static void usb_ibmcam_adjust_picture(struct usb_ibmcam *ibmcam)
+static void ibmcam_adjust_picture(uvd_t *uvd)
 {
-	usb_ibmcam_adjust_contrast(ibmcam);
-	usb_ibmcam_set_brightness(ibmcam);
-	if (ibmcam->camera_model == IBMCAM_MODEL_2) {
-		usb_ibmcam_model2_set_hue(ibmcam);
-	}
+	ibmcam_adjust_contrast(uvd);
+	ibmcam_set_brightness(uvd);
+	ibmcam_set_hue(uvd);
 }
 
-static int usb_ibmcam_model1_setup(struct usb_ibmcam *ibmcam)
+static int ibmcam_model1_setup(uvd_t *uvd)
 {
 	const int ntries = 5;
 	int i;
 
-	usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0128);
-	usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0100);
-	usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0100);	/* LED On  */
-	usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0100);
-	usb_ibmcam_veio(ibmcam, 0, 0x81, 0x0100);	/* LED Off */
-	usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0100);
-	usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0100);	/* LED On  */
-	usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0108);
-
-	usb_ibmcam_veio(ibmcam, 0, 0x03, 0x0112);
-	usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0115);
-	usb_ibmcam_veio(ibmcam, 0, 0x06, 0x0115);
-	usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0116);
-	usb_ibmcam_veio(ibmcam, 0, 0x44, 0x0116);
-	usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0116);
-	usb_ibmcam_veio(ibmcam, 0, 0x40, 0x0116);
-	usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0115);
-	usb_ibmcam_veio(ibmcam, 0, 0x0e, 0x0115);
-	usb_ibmcam_veio(ibmcam, 0, 0x19, 0x012c);
-
-	usb_ibmcam_Packet_Format1(ibmcam, 0x00, 0x1e);
-	usb_ibmcam_Packet_Format1(ibmcam, 0x39, 0x0d);
-	usb_ibmcam_Packet_Format1(ibmcam, 0x39, 0x09);
-	usb_ibmcam_Packet_Format1(ibmcam, 0x3b, 0x00);
-	usb_ibmcam_Packet_Format1(ibmcam, 0x28, 0x22);
-	usb_ibmcam_Packet_Format1(ibmcam, light_27, 0);
-	usb_ibmcam_Packet_Format1(ibmcam, 0x2b, 0x1f);
-	usb_ibmcam_Packet_Format1(ibmcam, 0x39, 0x08);
+	ibmcam_veio(uvd, 1, 0x00, 0x0128);
+	ibmcam_veio(uvd, 1, 0x00, 0x0100);
+	ibmcam_veio(uvd, 0, 0x01, 0x0100);	/* LED On  */
+	ibmcam_veio(uvd, 1, 0x00, 0x0100);
+	ibmcam_veio(uvd, 0, 0x81, 0x0100);	/* LED Off */
+	ibmcam_veio(uvd, 1, 0x00, 0x0100);
+	ibmcam_veio(uvd, 0, 0x01, 0x0100);	/* LED On  */
+	ibmcam_veio(uvd, 0, 0x01, 0x0108);
+
+	ibmcam_veio(uvd, 0, 0x03, 0x0112);
+	ibmcam_veio(uvd, 1, 0x00, 0x0115);
+	ibmcam_veio(uvd, 0, 0x06, 0x0115);
+	ibmcam_veio(uvd, 1, 0x00, 0x0116);
+	ibmcam_veio(uvd, 0, 0x44, 0x0116);
+	ibmcam_veio(uvd, 1, 0x00, 0x0116);
+	ibmcam_veio(uvd, 0, 0x40, 0x0116);
+	ibmcam_veio(uvd, 1, 0x00, 0x0115);
+	ibmcam_veio(uvd, 0, 0x0e, 0x0115);
+	ibmcam_veio(uvd, 0, 0x19, 0x012c);
+
+	ibmcam_Packet_Format1(uvd, 0x00, 0x1e);
+	ibmcam_Packet_Format1(uvd, 0x39, 0x0d);
+	ibmcam_Packet_Format1(uvd, 0x39, 0x09);
+	ibmcam_Packet_Format1(uvd, 0x3b, 0x00);
+	ibmcam_Packet_Format1(uvd, 0x28, 0x22);
+	ibmcam_Packet_Format1(uvd, light_27, 0);
+	ibmcam_Packet_Format1(uvd, 0x2b, 0x1f);
+	ibmcam_Packet_Format1(uvd, 0x39, 0x08);
 
 	for (i=0; i < ntries; i++)
-		usb_ibmcam_Packet_Format1(ibmcam, 0x2c, 0x00);
+		ibmcam_Packet_Format1(uvd, 0x2c, 0x00);
 
 	for (i=0; i < ntries; i++)
-		usb_ibmcam_Packet_Format1(ibmcam, 0x30, 0x14);
+		ibmcam_Packet_Format1(uvd, 0x30, 0x14);
 
-	usb_ibmcam_PacketFormat2(ibmcam, 0x39, 0x02);
-	usb_ibmcam_PacketFormat2(ibmcam, 0x01, 0xe1);
-	usb_ibmcam_PacketFormat2(ibmcam, 0x02, 0xcd);
-	usb_ibmcam_PacketFormat2(ibmcam, 0x03, 0xcd);
-	usb_ibmcam_PacketFormat2(ibmcam, 0x04, 0xfa);
-	usb_ibmcam_PacketFormat2(ibmcam, 0x3f, 0xff);
-	usb_ibmcam_PacketFormat2(ibmcam, 0x39, 0x00);
-
-	usb_ibmcam_PacketFormat2(ibmcam, 0x39, 0x02);
-	usb_ibmcam_PacketFormat2(ibmcam, 0x0a, 0x37);
-	usb_ibmcam_PacketFormat2(ibmcam, 0x0b, 0xb8);
-	usb_ibmcam_PacketFormat2(ibmcam, 0x0c, 0xf3);
-	usb_ibmcam_PacketFormat2(ibmcam, 0x0d, 0xe3);
-	usb_ibmcam_PacketFormat2(ibmcam, 0x0e, 0x0d);
-	usb_ibmcam_PacketFormat2(ibmcam, 0x0f, 0xf2);
-	usb_ibmcam_PacketFormat2(ibmcam, 0x10, 0xd5);
-	usb_ibmcam_PacketFormat2(ibmcam, 0x11, 0xba);
-	usb_ibmcam_PacketFormat2(ibmcam, 0x12, 0x53);
-	usb_ibmcam_PacketFormat2(ibmcam, 0x3f, 0xff);
-	usb_ibmcam_PacketFormat2(ibmcam, 0x39, 0x00);
-
-	usb_ibmcam_PacketFormat2(ibmcam, 0x39, 0x02);
-	usb_ibmcam_PacketFormat2(ibmcam, 0x16, 0x00);
-	usb_ibmcam_PacketFormat2(ibmcam, 0x17, 0x28);
-	usb_ibmcam_PacketFormat2(ibmcam, 0x18, 0x7d);
-	usb_ibmcam_PacketFormat2(ibmcam, 0x19, 0xbe);
-	usb_ibmcam_PacketFormat2(ibmcam, 0x3f, 0xff);
-	usb_ibmcam_PacketFormat2(ibmcam, 0x39, 0x00);
+	ibmcam_PacketFormat2(uvd, 0x39, 0x02);
+	ibmcam_PacketFormat2(uvd, 0x01, 0xe1);
+	ibmcam_PacketFormat2(uvd, 0x02, 0xcd);
+	ibmcam_PacketFormat2(uvd, 0x03, 0xcd);
+	ibmcam_PacketFormat2(uvd, 0x04, 0xfa);
+	ibmcam_PacketFormat2(uvd, 0x3f, 0xff);
+	ibmcam_PacketFormat2(uvd, 0x39, 0x00);
+
+	ibmcam_PacketFormat2(uvd, 0x39, 0x02);
+	ibmcam_PacketFormat2(uvd, 0x0a, 0x37);
+	ibmcam_PacketFormat2(uvd, 0x0b, 0xb8);
+	ibmcam_PacketFormat2(uvd, 0x0c, 0xf3);
+	ibmcam_PacketFormat2(uvd, 0x0d, 0xe3);
+	ibmcam_PacketFormat2(uvd, 0x0e, 0x0d);
+	ibmcam_PacketFormat2(uvd, 0x0f, 0xf2);
+	ibmcam_PacketFormat2(uvd, 0x10, 0xd5);
+	ibmcam_PacketFormat2(uvd, 0x11, 0xba);
+	ibmcam_PacketFormat2(uvd, 0x12, 0x53);
+	ibmcam_PacketFormat2(uvd, 0x3f, 0xff);
+	ibmcam_PacketFormat2(uvd, 0x39, 0x00);
+
+	ibmcam_PacketFormat2(uvd, 0x39, 0x02);
+	ibmcam_PacketFormat2(uvd, 0x16, 0x00);
+	ibmcam_PacketFormat2(uvd, 0x17, 0x28);
+	ibmcam_PacketFormat2(uvd, 0x18, 0x7d);
+	ibmcam_PacketFormat2(uvd, 0x19, 0xbe);
+	ibmcam_PacketFormat2(uvd, 0x3f, 0xff);
+	ibmcam_PacketFormat2(uvd, 0x39, 0x00);
 
 	for (i=0; i < ntries; i++)
-		usb_ibmcam_Packet_Format1(ibmcam, 0x00, 0x18);
+		ibmcam_Packet_Format1(uvd, 0x00, 0x18);
 	for (i=0; i < ntries; i++)
-		usb_ibmcam_Packet_Format1(ibmcam, 0x13, 0x18);
+		ibmcam_Packet_Format1(uvd, 0x13, 0x18);
 	for (i=0; i < ntries; i++)
-		usb_ibmcam_Packet_Format1(ibmcam, 0x14, 0x06);
+		ibmcam_Packet_Format1(uvd, 0x14, 0x06);
 
 	/* This is default brightness */
 	for (i=0; i < ntries; i++)
-		usb_ibmcam_Packet_Format1(ibmcam, 0x31, 0x37);
+		ibmcam_Packet_Format1(uvd, 0x31, 0x37);
 	for (i=0; i < ntries; i++)
-		usb_ibmcam_Packet_Format1(ibmcam, 0x32, 0x46);
+		ibmcam_Packet_Format1(uvd, 0x32, 0x46);
 	for (i=0; i < ntries; i++)
-		usb_ibmcam_Packet_Format1(ibmcam, 0x33, 0x55);
+		ibmcam_Packet_Format1(uvd, 0x33, 0x55);
 
-	usb_ibmcam_Packet_Format1(ibmcam, 0x2e, 0x04);
+	ibmcam_Packet_Format1(uvd, 0x2e, 0x04);
 	for (i=0; i < ntries; i++)
-		usb_ibmcam_Packet_Format1(ibmcam, 0x2d, 0x04);
+		ibmcam_Packet_Format1(uvd, 0x2d, 0x04);
 	for (i=0; i < ntries; i++)
-		usb_ibmcam_Packet_Format1(ibmcam, 0x29, 0x80);
-	usb_ibmcam_Packet_Format1(ibmcam, 0x2c, 0x01);
-	usb_ibmcam_Packet_Format1(ibmcam, 0x30, 0x17);
-	usb_ibmcam_Packet_Format1(ibmcam, 0x39, 0x08);
+		ibmcam_Packet_Format1(uvd, 0x29, 0x80);
+	ibmcam_Packet_Format1(uvd, 0x2c, 0x01);
+	ibmcam_Packet_Format1(uvd, 0x30, 0x17);
+	ibmcam_Packet_Format1(uvd, 0x39, 0x08);
 	for (i=0; i < ntries; i++)
-		usb_ibmcam_Packet_Format1(ibmcam, 0x34, 0x00);
+		ibmcam_Packet_Format1(uvd, 0x34, 0x00);
 
-	usb_ibmcam_veio(ibmcam, 0, 0x00, 0x0101);
-	usb_ibmcam_veio(ibmcam, 0, 0x00, 0x010a);
+	ibmcam_veio(uvd, 0, 0x00, 0x0101);
+	ibmcam_veio(uvd, 0, 0x00, 0x010a);
 
-	switch (videosize) {
+	switch (uvd->videosize) {
 	case VIDEOSIZE_128x96:
-		usb_ibmcam_veio(ibmcam, 0, 0x80, 0x0103);
-		usb_ibmcam_veio(ibmcam, 0, 0x60, 0x0105);
-		usb_ibmcam_veio(ibmcam, 0, 0x0c, 0x010b);
-		usb_ibmcam_veio(ibmcam, 0, 0x04, 0x011b);	/* Same everywhere */
-		usb_ibmcam_veio(ibmcam, 0, 0x0b, 0x011d);
-		usb_ibmcam_veio(ibmcam, 0, 0x00, 0x011e);	/* Same everywhere */
-		usb_ibmcam_veio(ibmcam, 0, 0x00, 0x0129);
+		ibmcam_veio(uvd, 0, 0x80, 0x0103);
+		ibmcam_veio(uvd, 0, 0x60, 0x0105);
+		ibmcam_veio(uvd, 0, 0x0c, 0x010b);
+		ibmcam_veio(uvd, 0, 0x04, 0x011b);	/* Same everywhere */
+		ibmcam_veio(uvd, 0, 0x0b, 0x011d);
+		ibmcam_veio(uvd, 0, 0x00, 0x011e);	/* Same everywhere */
+		ibmcam_veio(uvd, 0, 0x00, 0x0129);
 		break;
 	case VIDEOSIZE_176x144:
-		usb_ibmcam_veio(ibmcam, 0, 0xb0, 0x0103);
-		usb_ibmcam_veio(ibmcam, 0, 0x8f, 0x0105);
-		usb_ibmcam_veio(ibmcam, 0, 0x06, 0x010b);
-		usb_ibmcam_veio(ibmcam, 0, 0x04, 0x011b);	/* Same everywhere */
-		usb_ibmcam_veio(ibmcam, 0, 0x0d, 0x011d);
-		usb_ibmcam_veio(ibmcam, 0, 0x00, 0x011e);	/* Same everywhere */
-		usb_ibmcam_veio(ibmcam, 0, 0x03, 0x0129);
+		ibmcam_veio(uvd, 0, 0xb0, 0x0103);
+		ibmcam_veio(uvd, 0, 0x8f, 0x0105);
+		ibmcam_veio(uvd, 0, 0x06, 0x010b);
+		ibmcam_veio(uvd, 0, 0x04, 0x011b);	/* Same everywhere */
+		ibmcam_veio(uvd, 0, 0x0d, 0x011d);
+		ibmcam_veio(uvd, 0, 0x00, 0x011e);	/* Same everywhere */
+		ibmcam_veio(uvd, 0, 0x03, 0x0129);
 		break;
 	case VIDEOSIZE_352x288:
-		usb_ibmcam_veio(ibmcam, 0, 0xb0, 0x0103);
-		usb_ibmcam_veio(ibmcam, 0, 0x90, 0x0105);
-		usb_ibmcam_veio(ibmcam, 0, 0x02, 0x010b);
-		usb_ibmcam_veio(ibmcam, 0, 0x04, 0x011b);	/* Same everywhere */
-		usb_ibmcam_veio(ibmcam, 0, 0x05, 0x011d);
-		usb_ibmcam_veio(ibmcam, 0, 0x00, 0x011e);	/* Same everywhere */
-		usb_ibmcam_veio(ibmcam, 0, 0x00, 0x0129);
+		ibmcam_veio(uvd, 0, 0xb0, 0x0103);
+		ibmcam_veio(uvd, 0, 0x90, 0x0105);
+		ibmcam_veio(uvd, 0, 0x02, 0x010b);
+		ibmcam_veio(uvd, 0, 0x04, 0x011b);	/* Same everywhere */
+		ibmcam_veio(uvd, 0, 0x05, 0x011d);
+		ibmcam_veio(uvd, 0, 0x00, 0x011e);	/* Same everywhere */
+		ibmcam_veio(uvd, 0, 0x00, 0x0129);
 		break;
 	}
 
-	usb_ibmcam_veio(ibmcam, 0, 0xff, 0x012b);
+	ibmcam_veio(uvd, 0, 0xff, 0x012b);
 
 	/* This is another brightness - don't know why */
 	for (i=0; i < ntries; i++)
-		usb_ibmcam_Packet_Format1(ibmcam, 0x31, 0xc3);
+		ibmcam_Packet_Format1(uvd, 0x31, 0xc3);
 	for (i=0; i < ntries; i++)
-		usb_ibmcam_Packet_Format1(ibmcam, 0x32, 0xd2);
+		ibmcam_Packet_Format1(uvd, 0x32, 0xd2);
 	for (i=0; i < ntries; i++)
-		usb_ibmcam_Packet_Format1(ibmcam, 0x33, 0xe1);
+		ibmcam_Packet_Format1(uvd, 0x33, 0xe1);
 
 	/* Default contrast */
 	for (i=0; i < ntries; i++)
-		usb_ibmcam_Packet_Format1(ibmcam, contrast_14, 0x0a);
+		ibmcam_Packet_Format1(uvd, contrast_14, 0x0a);
 
 	/* Default sharpness */
 	for (i=0; i < 2; i++)
-		usb_ibmcam_PacketFormat2(ibmcam, sharp_13, 0x1a);	/* Level 4 FIXME */
+		ibmcam_PacketFormat2(uvd, sharp_13, 0x1a);	/* Level 4 FIXME */
 
 	/* Default lighting conditions */
-	usb_ibmcam_Packet_Format1(ibmcam, light_27, lighting); /* 0=Bright 2=Low */
+	ibmcam_Packet_Format1(uvd, light_27, lighting); /* 0=Bright 2=Low */
 
 	/* Assorted init */
 
-	switch (videosize) {
+	switch (uvd->videosize) {
 	case VIDEOSIZE_128x96:
-		usb_ibmcam_Packet_Format1(ibmcam, 0x2b, 0x1e);
-		usb_ibmcam_veio(ibmcam, 0, 0xc9, 0x0119);	/* Same everywhere */
-		usb_ibmcam_veio(ibmcam, 0, 0x80, 0x0109);	/* Same everywhere */
-		usb_ibmcam_veio(ibmcam, 0, 0x36, 0x0102);
-		usb_ibmcam_veio(ibmcam, 0, 0x1a, 0x0104);
-		usb_ibmcam_veio(ibmcam, 0, 0x04, 0x011a);	/* Same everywhere */
-		usb_ibmcam_veio(ibmcam, 0, 0x2b, 0x011c);
-		usb_ibmcam_veio(ibmcam, 0, 0x23, 0x012a);	/* Same everywhere */
+		ibmcam_Packet_Format1(uvd, 0x2b, 0x1e);
+		ibmcam_veio(uvd, 0, 0xc9, 0x0119);	/* Same everywhere */
+		ibmcam_veio(uvd, 0, 0x80, 0x0109);	/* Same everywhere */
+		ibmcam_veio(uvd, 0, 0x36, 0x0102);
+		ibmcam_veio(uvd, 0, 0x1a, 0x0104);
+		ibmcam_veio(uvd, 0, 0x04, 0x011a);	/* Same everywhere */
+		ibmcam_veio(uvd, 0, 0x2b, 0x011c);
+		ibmcam_veio(uvd, 0, 0x23, 0x012a);	/* Same everywhere */
 #if 0
-		usb_ibmcam_veio(ibmcam, 0, 0x00, 0x0106);
-		usb_ibmcam_veio(ibmcam, 0, 0x38, 0x0107);
+		ibmcam_veio(uvd, 0, 0x00, 0x0106);
+		ibmcam_veio(uvd, 0, 0x38, 0x0107);
 #else
-		usb_ibmcam_veio(ibmcam, 0, 0x02, 0x0106);
-		usb_ibmcam_veio(ibmcam, 0, 0x2a, 0x0107);
+		ibmcam_veio(uvd, 0, 0x02, 0x0106);
+		ibmcam_veio(uvd, 0, 0x2a, 0x0107);
 #endif
 		break;
 	case VIDEOSIZE_176x144:
-		usb_ibmcam_Packet_Format1(ibmcam, 0x2b, 0x1e);
-		usb_ibmcam_veio(ibmcam, 0, 0xc9, 0x0119);	/* Same everywhere */
-		usb_ibmcam_veio(ibmcam, 0, 0x80, 0x0109);	/* Same everywhere */
-		usb_ibmcam_veio(ibmcam, 0, 0x04, 0x0102);
-		usb_ibmcam_veio(ibmcam, 0, 0x02, 0x0104);
-		usb_ibmcam_veio(ibmcam, 0, 0x04, 0x011a);	/* Same everywhere */
-		usb_ibmcam_veio(ibmcam, 0, 0x2b, 0x011c);
-		usb_ibmcam_veio(ibmcam, 0, 0x23, 0x012a);	/* Same everywhere */
-		usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0106);
-		usb_ibmcam_veio(ibmcam, 0, 0xca, 0x0107);
+		ibmcam_Packet_Format1(uvd, 0x2b, 0x1e);
+		ibmcam_veio(uvd, 0, 0xc9, 0x0119);	/* Same everywhere */
+		ibmcam_veio(uvd, 0, 0x80, 0x0109);	/* Same everywhere */
+		ibmcam_veio(uvd, 0, 0x04, 0x0102);
+		ibmcam_veio(uvd, 0, 0x02, 0x0104);
+		ibmcam_veio(uvd, 0, 0x04, 0x011a);	/* Same everywhere */
+		ibmcam_veio(uvd, 0, 0x2b, 0x011c);
+		ibmcam_veio(uvd, 0, 0x23, 0x012a);	/* Same everywhere */
+		ibmcam_veio(uvd, 0, 0x01, 0x0106);
+		ibmcam_veio(uvd, 0, 0xca, 0x0107);
 		break;
 	case VIDEOSIZE_352x288:
-		usb_ibmcam_Packet_Format1(ibmcam, 0x2b, 0x1f);
-		usb_ibmcam_veio(ibmcam, 0, 0xc9, 0x0119);	/* Same everywhere */
-		usb_ibmcam_veio(ibmcam, 0, 0x80, 0x0109);	/* Same everywhere */
-		usb_ibmcam_veio(ibmcam, 0, 0x08, 0x0102);
-		usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0104);
-		usb_ibmcam_veio(ibmcam, 0, 0x04, 0x011a);	/* Same everywhere */
-		usb_ibmcam_veio(ibmcam, 0, 0x2f, 0x011c);
-		usb_ibmcam_veio(ibmcam, 0, 0x23, 0x012a);	/* Same everywhere */
-		usb_ibmcam_veio(ibmcam, 0, 0x03, 0x0106);
-		usb_ibmcam_veio(ibmcam, 0, 0xf6, 0x0107);
-		break;
-	}
-	return IBMCAM_IS_OPERATIONAL(ibmcam);
-}
-
-static int usb_ibmcam_model2_setup(struct usb_ibmcam *ibmcam)
-{
-	usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0100);	/* LED on */
-	usb_ibmcam_veio(ibmcam, 1, 0x0000, 0x0116);
-	usb_ibmcam_veio(ibmcam, 0, 0x0060, 0x0116);
-	usb_ibmcam_veio(ibmcam, 0, 0x0002, 0x0112);
-	usb_ibmcam_veio(ibmcam, 0, 0x00bc, 0x012c);
-	usb_ibmcam_veio(ibmcam, 0, 0x0008, 0x012b);
-	usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0108);
-	usb_ibmcam_veio(ibmcam, 0, 0x0001, 0x0133);
-	usb_ibmcam_veio(ibmcam, 0, 0x0001, 0x0102);
-	switch (videosize) {
+		ibmcam_Packet_Format1(uvd, 0x2b, 0x1f);
+		ibmcam_veio(uvd, 0, 0xc9, 0x0119);	/* Same everywhere */
+		ibmcam_veio(uvd, 0, 0x80, 0x0109);	/* Same everywhere */
+		ibmcam_veio(uvd, 0, 0x08, 0x0102);
+		ibmcam_veio(uvd, 0, 0x01, 0x0104);
+		ibmcam_veio(uvd, 0, 0x04, 0x011a);	/* Same everywhere */
+		ibmcam_veio(uvd, 0, 0x2f, 0x011c);
+		ibmcam_veio(uvd, 0, 0x23, 0x012a);	/* Same everywhere */
+		ibmcam_veio(uvd, 0, 0x03, 0x0106);
+		ibmcam_veio(uvd, 0, 0xf6, 0x0107);
+		break;
+	}
+	return (CAMERA_IS_OPERATIONAL(uvd) ? 0 : -EFAULT);
+}
+
+static int ibmcam_model2_setup(uvd_t *uvd)
+{
+	ibmcam_veio(uvd, 0, 0x0000, 0x0100);	/* LED on */
+	ibmcam_veio(uvd, 1, 0x0000, 0x0116);
+	ibmcam_veio(uvd, 0, 0x0060, 0x0116);
+	ibmcam_veio(uvd, 0, 0x0002, 0x0112);
+	ibmcam_veio(uvd, 0, 0x00bc, 0x012c);
+	ibmcam_veio(uvd, 0, 0x0008, 0x012b);
+	ibmcam_veio(uvd, 0, 0x0000, 0x0108);
+	ibmcam_veio(uvd, 0, 0x0001, 0x0133);
+	ibmcam_veio(uvd, 0, 0x0001, 0x0102);
+	switch (uvd->videosize) {
 	case VIDEOSIZE_176x144:
-		usb_ibmcam_veio(ibmcam, 0, 0x002c, 0x0103);	/* All except 320x240 */
-		usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0104);	/* Same */
-		usb_ibmcam_veio(ibmcam, 0, 0x0024, 0x0105);	/* 176x144, 352x288 */
-		usb_ibmcam_veio(ibmcam, 0, 0x00b9, 0x010a);	/* Unique to this mode */
-		usb_ibmcam_veio(ibmcam, 0, 0x0038, 0x0119);	/* Unique to this mode */
-		usb_ibmcam_veio(ibmcam, 0, 0x0003, 0x0106);	/* Same */
-		usb_ibmcam_veio(ibmcam, 0, 0x0090, 0x0107);	/* Unique to every mode*/
+		ibmcam_veio(uvd, 0, 0x002c, 0x0103);	/* All except 320x240 */
+		ibmcam_veio(uvd, 0, 0x0000, 0x0104);	/* Same */
+		ibmcam_veio(uvd, 0, 0x0024, 0x0105);	/* 176x144, 352x288 */
+		ibmcam_veio(uvd, 0, 0x00b9, 0x010a);	/* Unique to this mode */
+		ibmcam_veio(uvd, 0, 0x0038, 0x0119);	/* Unique to this mode */
+		ibmcam_veio(uvd, 0, 0x0003, 0x0106);	/* Same */
+		ibmcam_veio(uvd, 0, 0x0090, 0x0107);	/* Unique to every mode*/
 		break;
 	case VIDEOSIZE_320x240:
-		usb_ibmcam_veio(ibmcam, 0, 0x0028, 0x0103);	/* Unique to this mode */
-		usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0104);	/* Same */
-		usb_ibmcam_veio(ibmcam, 0, 0x001e, 0x0105);	/* 320x240, 352x240 */
-		usb_ibmcam_veio(ibmcam, 0, 0x0039, 0x010a);	/* All except 176x144 */
-		usb_ibmcam_veio(ibmcam, 0, 0x0070, 0x0119);	/* All except 176x144 */
-		usb_ibmcam_veio(ibmcam, 0, 0x0003, 0x0106);	/* Same */
-		usb_ibmcam_veio(ibmcam, 0, 0x0098, 0x0107);	/* Unique to every mode*/
+		ibmcam_veio(uvd, 0, 0x0028, 0x0103);	/* Unique to this mode */
+		ibmcam_veio(uvd, 0, 0x0000, 0x0104);	/* Same */
+		ibmcam_veio(uvd, 0, 0x001e, 0x0105);	/* 320x240, 352x240 */
+		ibmcam_veio(uvd, 0, 0x0039, 0x010a);	/* All except 176x144 */
+		ibmcam_veio(uvd, 0, 0x0070, 0x0119);	/* All except 176x144 */
+		ibmcam_veio(uvd, 0, 0x0003, 0x0106);	/* Same */
+		ibmcam_veio(uvd, 0, 0x0098, 0x0107);	/* Unique to every mode*/
 		break;
 	case VIDEOSIZE_352x240:
-		usb_ibmcam_veio(ibmcam, 0, 0x002c, 0x0103);	/* All except 320x240 */
-		usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0104);	/* Same */
-		usb_ibmcam_veio(ibmcam, 0, 0x001e, 0x0105);	/* 320x240, 352x240 */
-		usb_ibmcam_veio(ibmcam, 0, 0x0039, 0x010a);	/* All except 176x144 */
-		usb_ibmcam_veio(ibmcam, 0, 0x0070, 0x0119);	/* All except 176x144 */
-		usb_ibmcam_veio(ibmcam, 0, 0x0003, 0x0106);	/* Same */
-		usb_ibmcam_veio(ibmcam, 0, 0x00da, 0x0107);	/* Unique to every mode*/
+		ibmcam_veio(uvd, 0, 0x002c, 0x0103);	/* All except 320x240 */
+		ibmcam_veio(uvd, 0, 0x0000, 0x0104);	/* Same */
+		ibmcam_veio(uvd, 0, 0x001e, 0x0105);	/* 320x240, 352x240 */
+		ibmcam_veio(uvd, 0, 0x0039, 0x010a);	/* All except 176x144 */
+		ibmcam_veio(uvd, 0, 0x0070, 0x0119);	/* All except 176x144 */
+		ibmcam_veio(uvd, 0, 0x0003, 0x0106);	/* Same */
+		ibmcam_veio(uvd, 0, 0x00da, 0x0107);	/* Unique to every mode*/
 		break;
 	case VIDEOSIZE_352x288:
-		usb_ibmcam_veio(ibmcam, 0, 0x002c, 0x0103);	/* All except 320x240 */
-		usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0104);	/* Same */
-		usb_ibmcam_veio(ibmcam, 0, 0x0024, 0x0105);	/* 176x144, 352x288 */
-		usb_ibmcam_veio(ibmcam, 0, 0x0039, 0x010a);	/* All except 176x144 */
-		usb_ibmcam_veio(ibmcam, 0, 0x0070, 0x0119);	/* All except 176x144 */
-		usb_ibmcam_veio(ibmcam, 0, 0x0003, 0x0106);	/* Same */
-		usb_ibmcam_veio(ibmcam, 0, 0x00fe, 0x0107);	/* Unique to every mode*/
+		ibmcam_veio(uvd, 0, 0x002c, 0x0103);	/* All except 320x240 */
+		ibmcam_veio(uvd, 0, 0x0000, 0x0104);	/* Same */
+		ibmcam_veio(uvd, 0, 0x0024, 0x0105);	/* 176x144, 352x288 */
+		ibmcam_veio(uvd, 0, 0x0039, 0x010a);	/* All except 176x144 */
+		ibmcam_veio(uvd, 0, 0x0070, 0x0119);	/* All except 176x144 */
+		ibmcam_veio(uvd, 0, 0x0003, 0x0106);	/* Same */
+		ibmcam_veio(uvd, 0, 0x00fe, 0x0107);	/* Unique to every mode*/
 		break;
 	}
-	return IBMCAM_IS_OPERATIONAL(ibmcam);
+	return (CAMERA_IS_OPERATIONAL(uvd) ? 0 : -EFAULT);
 }
 
 /*
- * usb_ibmcam_model1_setup_after_video_if()
+ * ibmcam_model1_setup_after_video_if()
  *
  * This code adds finishing touches to the video data interface.
  * Here we configure the frame rate and turn on the LED.
  */
-static void usb_ibmcam_model1_setup_after_video_if(struct usb_ibmcam *ibmcam)
+static void ibmcam_model1_setup_after_video_if(uvd_t *uvd)
 {
 	unsigned short internal_frame_rate;
 
 	RESTRICT_TO_RANGE(framerate, FRAMERATE_MIN, FRAMERATE_MAX);
 	internal_frame_rate = FRAMERATE_MAX - framerate; /* 0=Fast 6=Slow */
-	usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0100);	/* LED On  */
-	usb_ibmcam_veio(ibmcam, 0, internal_frame_rate, 0x0111);
-	usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0114);
-	usb_ibmcam_veio(ibmcam, 0, 0xc0, 0x010c);
+	ibmcam_veio(uvd, 0, 0x01, 0x0100);	/* LED On  */
+	ibmcam_veio(uvd, 0, internal_frame_rate, 0x0111);
+	ibmcam_veio(uvd, 0, 0x01, 0x0114);
+	ibmcam_veio(uvd, 0, 0xc0, 0x010c);
 }
 
-static void usb_ibmcam_model2_setup_after_video_if(struct usb_ibmcam *ibmcam)
+static void ibmcam_model2_setup_after_video_if(uvd_t *uvd)
 {
-	unsigned short setup_model2_rg, setup_model2_rg2, setup_model2_sat, setup_model2_yb;
+	unsigned short setup_model2_rg2, setup_model2_sat, setup_model2_yb;
 
-	usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0100);	/* LED on */
+	ibmcam_veio(uvd, 0, 0x0000, 0x0100);	/* LED on */
 
-	switch (videosize) {
+	switch (uvd->videosize) {
 	case VIDEOSIZE_176x144:
-		usb_ibmcam_veio(ibmcam, 0, 0x0050, 0x0111);
-		usb_ibmcam_veio(ibmcam, 0, 0x00d0, 0x0111);
+		ibmcam_veio(uvd, 0, 0x0050, 0x0111);
+		ibmcam_veio(uvd, 0, 0x00d0, 0x0111);
 		break;
 	case VIDEOSIZE_320x240:
 	case VIDEOSIZE_352x240:
 	case VIDEOSIZE_352x288:
-		usb_ibmcam_veio(ibmcam, 0, 0x0040, 0x0111);
-		usb_ibmcam_veio(ibmcam, 0, 0x00c0, 0x0111);
+		ibmcam_veio(uvd, 0, 0x0040, 0x0111);
+		ibmcam_veio(uvd, 0, 0x00c0, 0x0111);
 		break;
 	}
-	usb_ibmcam_veio(ibmcam, 0, 0x009b, 0x010f);
-	usb_ibmcam_veio(ibmcam, 0, 0x00bb, 0x010f);
+	ibmcam_veio(uvd, 0, 0x009b, 0x010f);
+	ibmcam_veio(uvd, 0, 0x00bb, 0x010f);
 
 	/*
 	 * Hardware settings, may affect CMOS sensor; not user controls!
@@ -1939,52 +2009,52 @@
 	 * 0x002c: hardware setting (related to scan lines)
 	 * 0x002e: stops video stream, probably important h/w setting
 	 */
-	usb_ibmcam_model2_Packet1(ibmcam, 0x000a, 0x005c);
-	usb_ibmcam_model2_Packet1(ibmcam, 0x0004, 0x0000);
-	usb_ibmcam_model2_Packet1(ibmcam, 0x0006, 0x00fb);
-	usb_ibmcam_model2_Packet1(ibmcam, 0x0008, 0x0000);
-	usb_ibmcam_model2_Packet1(ibmcam, 0x000c, 0x0009);
-	usb_ibmcam_model2_Packet1(ibmcam, 0x0012, 0x000a);
-	usb_ibmcam_model2_Packet1(ibmcam, 0x002a, 0x0000);
-	usb_ibmcam_model2_Packet1(ibmcam, 0x002c, 0x0000);
-	usb_ibmcam_model2_Packet1(ibmcam, 0x002e, 0x0008);
+	ibmcam_model2_Packet1(uvd, 0x000a, 0x005c);
+	ibmcam_model2_Packet1(uvd, 0x0004, 0x0000);
+	ibmcam_model2_Packet1(uvd, 0x0006, 0x00fb);
+	ibmcam_model2_Packet1(uvd, 0x0008, 0x0000);
+	ibmcam_model2_Packet1(uvd, 0x000c, 0x0009);
+	ibmcam_model2_Packet1(uvd, 0x0012, 0x000a);
+	ibmcam_model2_Packet1(uvd, 0x002a, 0x0000);
+	ibmcam_model2_Packet1(uvd, 0x002c, 0x0000);
+	ibmcam_model2_Packet1(uvd, 0x002e, 0x0008);
 
 	/*
 	 * Function 0x0030 pops up all over the place. Apparently
 	 * it is a hardware control register, with every bit assigned to
 	 * do something.
 	 */
-	usb_ibmcam_model2_Packet1(ibmcam, 0x0030, 0x0000);
+	ibmcam_model2_Packet1(uvd, 0x0030, 0x0000);
 
 	/*
 	 * Magic control of CMOS sensor. Only lower values like
 	 * 0-3 work, and picture shifts left or right. Don't change.
 	 */
-	switch (videosize) {
+	switch (uvd->videosize) {
 	case VIDEOSIZE_176x144:
-		usb_ibmcam_model2_Packet1(ibmcam, 0x0014, 0x0002);
-		usb_ibmcam_model2_Packet1(ibmcam, 0x0016, 0x0002); /* Horizontal shift */
-		usb_ibmcam_model2_Packet1(ibmcam, 0x0018, 0x004a); /* Another hardware setting */
+		ibmcam_model2_Packet1(uvd, 0x0014, 0x0002);
+		ibmcam_model2_Packet1(uvd, 0x0016, 0x0002); /* Horizontal shift */
+		ibmcam_model2_Packet1(uvd, 0x0018, 0x004a); /* Another hardware setting */
 		break;
 	case VIDEOSIZE_320x240:
-		usb_ibmcam_model2_Packet1(ibmcam, 0x0014, 0x0009);
-		usb_ibmcam_model2_Packet1(ibmcam, 0x0016, 0x0005); /* Horizontal shift */
-		usb_ibmcam_model2_Packet1(ibmcam, 0x0018, 0x0044); /* Another hardware setting */
+		ibmcam_model2_Packet1(uvd, 0x0014, 0x0009);
+		ibmcam_model2_Packet1(uvd, 0x0016, 0x0005); /* Horizontal shift */
+		ibmcam_model2_Packet1(uvd, 0x0018, 0x0044); /* Another hardware setting */
 		break;
 	case VIDEOSIZE_352x240:
 		/* This mode doesn't work as Windows programs it; changed to work */
-		usb_ibmcam_model2_Packet1(ibmcam, 0x0014, 0x0009); /* Windows sets this to 8 */
-		usb_ibmcam_model2_Packet1(ibmcam, 0x0016, 0x0003); /* Horizontal shift */
-		usb_ibmcam_model2_Packet1(ibmcam, 0x0018, 0x0044); /* Windows sets this to 0x0045 */
+		ibmcam_model2_Packet1(uvd, 0x0014, 0x0009); /* Windows sets this to 8 */
+		ibmcam_model2_Packet1(uvd, 0x0016, 0x0003); /* Horizontal shift */
+		ibmcam_model2_Packet1(uvd, 0x0018, 0x0044); /* Windows sets this to 0x0045 */
 		break;
 	case VIDEOSIZE_352x288:
-		usb_ibmcam_model2_Packet1(ibmcam, 0x0014, 0x0003);
-		usb_ibmcam_model2_Packet1(ibmcam, 0x0016, 0x0002); /* Horizontal shift */
-		usb_ibmcam_model2_Packet1(ibmcam, 0x0018, 0x004a); /* Another hardware setting */
+		ibmcam_model2_Packet1(uvd, 0x0014, 0x0003);
+		ibmcam_model2_Packet1(uvd, 0x0016, 0x0002); /* Horizontal shift */
+		ibmcam_model2_Packet1(uvd, 0x0018, 0x004a); /* Another hardware setting */
 		break;
 	}
 
-	usb_ibmcam_model2_Packet1(ibmcam, mod2_brightness, 0x005a);
+	ibmcam_model2_Packet1(uvd, mod2_brightness, 0x005a);
 
 	/*
 	 * We have our own frame rate setting varying from 0 (slowest) to 6 (fastest).
@@ -2008,7 +2078,7 @@
 
 		RESTRICT_TO_RANGE(framerate, FRAMERATE_MIN, FRAMERATE_MAX);
 		i_framerate = FRAMERATE_MAX - framerate + FRAMERATE_MIN;
-		switch (videosize) {
+		switch (uvd->videosize) {
 		case VIDEOSIZE_176x144:
 			hw_fps = 6 + i_framerate*4;
 			break;
@@ -2022,10 +2092,10 @@
 			hw_fps = 28 + i_framerate/2;
 			break;
 		}
-		if (debug > 0)
-			printk(KERN_DEBUG "Framerate (hardware): %hd.\n", hw_fps);
+		if (uvd->debug > 0)
+			info("Framerate (hardware): %hd.", hw_fps);
 		RESTRICT_TO_RANGE(hw_fps, 0, 31);
-		usb_ibmcam_model2_Packet1(ibmcam, mod2_set_framerate, hw_fps);
+		ibmcam_model2_Packet1(uvd, mod2_set_framerate, hw_fps);
 	}
 
 	/*
@@ -2034,28 +2104,22 @@
 	 * does not allow arbitrary values and apparently is a bit mask, to
 	 * be activated only at appropriate time. Don't change it randomly!
 	 */
-	switch (videosize) {
+	switch (uvd->videosize) {
 	case VIDEOSIZE_176x144:
-		usb_ibmcam_model2_Packet1(ibmcam, 0x0026, 0x00c2);
+		ibmcam_model2_Packet1(uvd, 0x0026, 0x00c2);
 		break;
 	case VIDEOSIZE_320x240:
-		usb_ibmcam_model2_Packet1(ibmcam, 0x0026, 0x0044);
+		ibmcam_model2_Packet1(uvd, 0x0026, 0x0044);
 		break;
 	case VIDEOSIZE_352x240:
-		usb_ibmcam_model2_Packet1(ibmcam, 0x0026, 0x0046);
+		ibmcam_model2_Packet1(uvd, 0x0026, 0x0046);
 		break;
 	case VIDEOSIZE_352x288:
-		usb_ibmcam_model2_Packet1(ibmcam, 0x0026, 0x0048);
+		ibmcam_model2_Packet1(uvd, 0x0026, 0x0048);
 		break;
 	}
 
-	usb_ibmcam_model2_Packet1(ibmcam, mod2_sensitivity, lighting);
-
-	if (init_model2_rg >= 0) {
-		RESTRICT_TO_RANGE(init_model2_rg, 0, 255);
-		setup_model2_rg = init_model2_rg;
-	} else
-		setup_model2_rg = 0x0070;
+	ibmcam_model2_Packet1(uvd, mod2_sensitivity, lighting);
 
 	if (init_model2_rg2 >= 0) {
 		RESTRICT_TO_RANGE(init_model2_rg2, 0, 255);
@@ -2075,806 +2139,1469 @@
 	} else
 		setup_model2_yb = 0x00a0;
 
-	usb_ibmcam_model2_Packet1(ibmcam, mod2_color_balance_rg2, setup_model2_rg2);
-	usb_ibmcam_model2_Packet1(ibmcam, mod2_saturation, setup_model2_sat);
-	usb_ibmcam_model2_Packet1(ibmcam, mod2_color_balance_yb, setup_model2_yb);
-	usb_ibmcam_model2_Packet1(ibmcam, mod2_color_balance_rg, setup_model2_rg);
+	ibmcam_model2_Packet1(uvd, mod2_color_balance_rg2, setup_model2_rg2);
+	ibmcam_model2_Packet1(uvd, mod2_saturation, setup_model2_sat);
+	ibmcam_model2_Packet1(uvd, mod2_color_balance_yb, setup_model2_yb);
+	ibmcam_model2_Packet1(uvd, mod2_hue, uvd->vpic.hue >> 9); /* 0 .. 7F */;
 
 	/* Hardware control command */
-	usb_ibmcam_model2_Packet1(ibmcam, 0x0030, 0x0004);
+	ibmcam_model2_Packet1(uvd, 0x0030, 0x0004);
 
-	usb_ibmcam_veio(ibmcam, 0, 0x00c0, 0x010c);	/* Go camera, go! */
-	usb_clear_halt(ibmcam->dev, ibmcam->video_endp);
+	ibmcam_veio(uvd, 0, 0x00c0, 0x010c);	/* Go camera, go! */
+	usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp));
 }
 
-/*
- * usb_ibmcam_setup_video_stop()
- *
- * This code tells camera to stop streaming. The interface remains
- * configured and bandwidth - claimed.
- */
-static void usb_ibmcam_setup_video_stop(struct usb_ibmcam *ibmcam)
+static void ibmcam_model4_setup_after_video_if(uvd_t *uvd)
 {
-	if (ibmcam->camera_model == IBMCAM_MODEL_1) {
-		usb_ibmcam_veio(ibmcam, 0, 0x00, 0x010c);
-		usb_ibmcam_veio(ibmcam, 0, 0x00, 0x010c);
-		usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0114);
-		usb_ibmcam_veio(ibmcam, 0, 0xc0, 0x010c);
-		usb_ibmcam_veio(ibmcam, 0, 0x00, 0x010c);
-		usb_ibmcam_send_FF_04_02(ibmcam);
-		usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0100);
-		usb_ibmcam_veio(ibmcam, 0, 0x81, 0x0100);	/* LED Off */
-	} else if (ibmcam->camera_model == IBMCAM_MODEL_2) {
-		usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x010c);	/* Stop the camera */
-
-		usb_ibmcam_model2_Packet1(ibmcam, 0x0030, 0x0004);
-
-		usb_ibmcam_veio(ibmcam, 0, 0x0080, 0x0100);	/* LED Off */
-		usb_ibmcam_veio(ibmcam, 0, 0x0020, 0x0111);
-		usb_ibmcam_veio(ibmcam, 0, 0x00a0, 0x0111);
-
-		usb_ibmcam_model2_Packet1(ibmcam, 0x0030, 0x0002);
-
-		usb_ibmcam_veio(ibmcam, 0, 0x0020, 0x0111);
-		usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0112);
-	}
-}
-
-/*
- * usb_ibmcam_reinit_iso()
- *
- * 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.
- *
- * History:
- * 1/2/00   Created.
- */
-static void usb_ibmcam_reinit_iso(struct usb_ibmcam *ibmcam, int do_stop)
-{
-	if (ibmcam->camera_model == IBMCAM_MODEL_1) {
-		if (do_stop)
-			usb_ibmcam_setup_video_stop(ibmcam);
-		usb_ibmcam_veio(ibmcam, 0, 0x0001, 0x0114);
-		usb_ibmcam_veio(ibmcam, 0, 0x00c0, 0x010c);
-		usb_clear_halt(ibmcam->dev, ibmcam->video_endp);
-		usb_ibmcam_model1_setup_after_video_if(ibmcam);
-	} else if (ibmcam->camera_model == IBMCAM_MODEL_2) {
-		usb_ibmcam_model2_setup_after_video_if(ibmcam);
+	switch (uvd->videosize) {
+	case VIDEOSIZE_128x96:
+		ibmcam_veio(uvd, 0, 0x0000, 0x0100);
+		ibmcam_veio(uvd, 0, 0x00c0, 0x0111);
+		ibmcam_veio(uvd, 0, 0x00bc, 0x012c);
+		ibmcam_veio(uvd, 0, 0x0080, 0x012b);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0108);
+		ibmcam_veio(uvd, 0, 0x0001, 0x0133);
+		ibmcam_veio(uvd, 0, 0x009b, 0x010f);
+		ibmcam_veio(uvd, 0, 0x00bb, 0x010f);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0038, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+		ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x000a, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x005c, 0x0127);
+		ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0004, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+		ibmcam_veio(uvd, 0, 0x00fb, 0x012e);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0130);
+		ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd055, 0x0124);
+		ibmcam_veio(uvd, 0, 0x000c, 0x0127);
+		ibmcam_veio(uvd, 0, 0x0009, 0x012e);
+		ibmcam_veio(uvd, 0, 0xaa28, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0012, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0008, 0x0127);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+		ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x002a, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0000, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+		ibmcam_veio(uvd, 0, 0xfffa, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0034, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+		ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0070, 0x0119);
+		ibmcam_veio(uvd, 0, 0x00d2, 0x0107);
+		ibmcam_veio(uvd, 0, 0x0003, 0x0106);
+		ibmcam_veio(uvd, 0, 0x005e, 0x0107);
+		ibmcam_veio(uvd, 0, 0x0003, 0x0106);
+		ibmcam_veio(uvd, 0, 0x00d0, 0x0111);
+		ibmcam_veio(uvd, 0, 0x0039, 0x010a);
+		ibmcam_veio(uvd, 0, 0x0001, 0x0102);
+		ibmcam_veio(uvd, 0, 0x0028, 0x0103);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0104);
+		ibmcam_veio(uvd, 0, 0x001e, 0x0105);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0016, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x000a, 0x0127);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+		ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0014, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0008, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012e);
+		ibmcam_veio(uvd, 0, 0x001a, 0x0130);
+		ibmcam_veio(uvd, 0, 0x8a0a, 0x0124);
+		ibmcam_veio(uvd, 0, 0x005a, 0x012d);
+		ibmcam_veio(uvd, 0, 0x9545, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x0127);
+		ibmcam_veio(uvd, 0, 0x0018, 0x012e);
+		ibmcam_veio(uvd, 0, 0x0043, 0x0130);
+		ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd055, 0x0124);
+		ibmcam_veio(uvd, 0, 0x001c, 0x0127);
+		ibmcam_veio(uvd, 0, 0x00eb, 0x012e);
+		ibmcam_veio(uvd, 0, 0xaa28, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0032, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+		ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0036, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0008, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+		ibmcam_veio(uvd, 0, 0xfffa, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x001e, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0017, 0x0127);
+		ibmcam_veio(uvd, 0, 0x0013, 0x012e);
+		ibmcam_veio(uvd, 0, 0x0031, 0x0130);
+		ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0017, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0078, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+		ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0038, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0004, 0x0127);
+		ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00c0, 0x010c);
+		break;
+	case VIDEOSIZE_160x120:
+		ibmcam_veio(uvd, 0, 0x0000, 0x0100);
+		ibmcam_veio(uvd, 0, 0x00c0, 0x0111);
+		ibmcam_veio(uvd, 0, 0x00bc, 0x012c);
+		ibmcam_veio(uvd, 0, 0x0080, 0x012b);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0108);
+		ibmcam_veio(uvd, 0, 0x0001, 0x0133);
+		ibmcam_veio(uvd, 0, 0x009b, 0x010f);
+		ibmcam_veio(uvd, 0, 0x00bb, 0x010f);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0038, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+		ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x000a, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x005c, 0x0127);
+		ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0004, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+		ibmcam_veio(uvd, 0, 0x00fb, 0x012e);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0130);
+		ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd055, 0x0124);
+		ibmcam_veio(uvd, 0, 0x000c, 0x0127);
+		ibmcam_veio(uvd, 0, 0x0009, 0x012e);
+		ibmcam_veio(uvd, 0, 0xaa28, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0012, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0008, 0x0127);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+		ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x002a, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0000, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+		ibmcam_veio(uvd, 0, 0xfffa, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0034, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+		ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0038, 0x0119);
+		ibmcam_veio(uvd, 0, 0x00d8, 0x0107);
+		ibmcam_veio(uvd, 0, 0x0002, 0x0106);
+		ibmcam_veio(uvd, 0, 0x00d0, 0x0111);
+		ibmcam_veio(uvd, 0, 0x00b9, 0x010a);
+		ibmcam_veio(uvd, 0, 0x0001, 0x0102);
+		ibmcam_veio(uvd, 0, 0x0028, 0x0103);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0104);
+		ibmcam_veio(uvd, 0, 0x001e, 0x0105);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0016, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x000b, 0x0127);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+		ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0014, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0008, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012e);
+		ibmcam_veio(uvd, 0, 0x001a, 0x0130);
+		ibmcam_veio(uvd, 0, 0x8a0a, 0x0124);
+		ibmcam_veio(uvd, 0, 0x005a, 0x012d);
+		ibmcam_veio(uvd, 0, 0x9545, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x0127);
+		ibmcam_veio(uvd, 0, 0x0018, 0x012e);
+		ibmcam_veio(uvd, 0, 0x0043, 0x0130);
+		ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd055, 0x0124);
+		ibmcam_veio(uvd, 0, 0x001c, 0x0127);
+		ibmcam_veio(uvd, 0, 0x00c7, 0x012e);
+		ibmcam_veio(uvd, 0, 0xaa28, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0032, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0025, 0x0127);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+		ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0036, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0008, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+		ibmcam_veio(uvd, 0, 0xfffa, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x001e, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0048, 0x0127);
+		ibmcam_veio(uvd, 0, 0x0035, 0x012e);
+		ibmcam_veio(uvd, 0, 0x00d0, 0x0130);
+		ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0048, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0090, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0001, 0x0127);
+		ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0038, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0004, 0x0127);
+		ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00c0, 0x010c);
+		break;
+	case VIDEOSIZE_176x144:
+		ibmcam_veio(uvd, 0, 0x0000, 0x0100);
+		ibmcam_veio(uvd, 0, 0x00c0, 0x0111);
+		ibmcam_veio(uvd, 0, 0x00bc, 0x012c);
+		ibmcam_veio(uvd, 0, 0x0080, 0x012b);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0108);
+		ibmcam_veio(uvd, 0, 0x0001, 0x0133);
+		ibmcam_veio(uvd, 0, 0x009b, 0x010f);
+		ibmcam_veio(uvd, 0, 0x00bb, 0x010f);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0038, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+		ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x000a, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x005c, 0x0127);
+		ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0004, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+		ibmcam_veio(uvd, 0, 0x00fb, 0x012e);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0130);
+		ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd055, 0x0124);
+		ibmcam_veio(uvd, 0, 0x000c, 0x0127);
+		ibmcam_veio(uvd, 0, 0x0009, 0x012e);
+		ibmcam_veio(uvd, 0, 0xaa28, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0012, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0008, 0x0127);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+		ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x002a, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0000, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+		ibmcam_veio(uvd, 0, 0xfffa, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0034, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+		ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0038, 0x0119);
+		ibmcam_veio(uvd, 0, 0x00d6, 0x0107);
+		ibmcam_veio(uvd, 0, 0x0003, 0x0106);
+		ibmcam_veio(uvd, 0, 0x0018, 0x0107);
+		ibmcam_veio(uvd, 0, 0x0003, 0x0106);
+		ibmcam_veio(uvd, 0, 0x00d0, 0x0111);
+		ibmcam_veio(uvd, 0, 0x00b9, 0x010a);
+		ibmcam_veio(uvd, 0, 0x0001, 0x0102);
+		ibmcam_veio(uvd, 0, 0x002c, 0x0103);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0104);
+		ibmcam_veio(uvd, 0, 0x0024, 0x0105);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0016, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0007, 0x0127);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+		ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0014, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0001, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012e);
+		ibmcam_veio(uvd, 0, 0x001a, 0x0130);
+		ibmcam_veio(uvd, 0, 0x8a0a, 0x0124);
+		ibmcam_veio(uvd, 0, 0x005e, 0x012d);
+		ibmcam_veio(uvd, 0, 0x9545, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x0127);
+		ibmcam_veio(uvd, 0, 0x0018, 0x012e);
+		ibmcam_veio(uvd, 0, 0x0049, 0x0130);
+		ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd055, 0x0124);
+		ibmcam_veio(uvd, 0, 0x001c, 0x0127);
+		ibmcam_veio(uvd, 0, 0x00c7, 0x012e);
+		ibmcam_veio(uvd, 0, 0xaa28, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0032, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0028, 0x0127);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+		ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0036, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0008, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+		ibmcam_veio(uvd, 0, 0xfffa, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x001e, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0010, 0x0127);
+		ibmcam_veio(uvd, 0, 0x0013, 0x012e);
+		ibmcam_veio(uvd, 0, 0x002a, 0x0130);
+		ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0010, 0x012d);
+		ibmcam_veio(uvd, 0, 0x006d, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0001, 0x0127);
+		ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0038, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0004, 0x0127);
+		ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00c0, 0x010c);
+		break;
+	case VIDEOSIZE_320x240:
+		ibmcam_veio(uvd, 0, 0x0000, 0x0100);
+		ibmcam_veio(uvd, 0, 0x00c0, 0x0111);
+		ibmcam_veio(uvd, 0, 0x00bc, 0x012c);
+		ibmcam_veio(uvd, 0, 0x0080, 0x012b);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0108);
+		ibmcam_veio(uvd, 0, 0x0001, 0x0133);
+		ibmcam_veio(uvd, 0, 0x009b, 0x010f);
+		ibmcam_veio(uvd, 0, 0x00bb, 0x010f);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0038, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+		ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x000a, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x005c, 0x0127);
+		ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0004, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+		ibmcam_veio(uvd, 0, 0x00fb, 0x012e);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0130);
+		ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd055, 0x0124);
+		ibmcam_veio(uvd, 0, 0x000c, 0x0127);
+		ibmcam_veio(uvd, 0, 0x0009, 0x012e);
+		ibmcam_veio(uvd, 0, 0xaa28, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0012, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0008, 0x0127);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+		ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x002a, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0000, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+		ibmcam_veio(uvd, 0, 0xfffa, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0034, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+		ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0070, 0x0119);
+		ibmcam_veio(uvd, 0, 0x00d2, 0x0107);
+		ibmcam_veio(uvd, 0, 0x0003, 0x0106);
+		ibmcam_veio(uvd, 0, 0x005e, 0x0107);
+		ibmcam_veio(uvd, 0, 0x0003, 0x0106);
+		ibmcam_veio(uvd, 0, 0x00d0, 0x0111);
+		ibmcam_veio(uvd, 0, 0x0039, 0x010a);
+		ibmcam_veio(uvd, 0, 0x0001, 0x0102);
+		ibmcam_veio(uvd, 0, 0x0028, 0x0103);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0104);
+		ibmcam_veio(uvd, 0, 0x001e, 0x0105);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0016, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x000a, 0x0127);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+		ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0014, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0008, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012e);
+		ibmcam_veio(uvd, 0, 0x001a, 0x0130);
+		ibmcam_veio(uvd, 0, 0x8a0a, 0x0124);
+		ibmcam_veio(uvd, 0, 0x005a, 0x012d);
+		ibmcam_veio(uvd, 0, 0x9545, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x0127);
+		ibmcam_veio(uvd, 0, 0x0018, 0x012e);
+		ibmcam_veio(uvd, 0, 0x0043, 0x0130);
+		ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd055, 0x0124);
+		ibmcam_veio(uvd, 0, 0x001c, 0x0127);
+		ibmcam_veio(uvd, 0, 0x00eb, 0x012e);
+		ibmcam_veio(uvd, 0, 0xaa28, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0032, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+		ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0036, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0008, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+		ibmcam_veio(uvd, 0, 0xfffa, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x001e, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0017, 0x0127);
+		ibmcam_veio(uvd, 0, 0x0013, 0x012e);
+		ibmcam_veio(uvd, 0, 0x0031, 0x0130);
+		ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0017, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0078, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+		ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0038, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0004, 0x0127);
+		ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00c0, 0x010c);
+		break;
+	case VIDEOSIZE_352x288:
+		ibmcam_veio(uvd, 0, 0x0000, 0x0100);
+		ibmcam_veio(uvd, 0, 0x00c0, 0x0111);
+		ibmcam_veio(uvd, 0, 0x00bc, 0x012c);
+		ibmcam_veio(uvd, 0, 0x0080, 0x012b);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0108);
+		ibmcam_veio(uvd, 0, 0x0001, 0x0133);
+		ibmcam_veio(uvd, 0, 0x009b, 0x010f);
+		ibmcam_veio(uvd, 0, 0x00bb, 0x010f);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0038, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+		ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x000a, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x005c, 0x0127);
+		ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0004, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+		ibmcam_veio(uvd, 0, 0x00fb, 0x012e);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0130);
+		ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd055, 0x0124);
+		ibmcam_veio(uvd, 0, 0x000c, 0x0127);
+		ibmcam_veio(uvd, 0, 0x0009, 0x012e);
+		ibmcam_veio(uvd, 0, 0xaa28, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0012, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0008, 0x0127);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+		ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x002a, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0000, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+		ibmcam_veio(uvd, 0, 0xfffa, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0034, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+		ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0070, 0x0119);
+		ibmcam_veio(uvd, 0, 0x00f2, 0x0107);
+		ibmcam_veio(uvd, 0, 0x0003, 0x0106);
+		ibmcam_veio(uvd, 0, 0x008c, 0x0107);
+		ibmcam_veio(uvd, 0, 0x0003, 0x0106);
+		ibmcam_veio(uvd, 0, 0x00c0, 0x0111);
+		ibmcam_veio(uvd, 0, 0x0039, 0x010a);
+		ibmcam_veio(uvd, 0, 0x0001, 0x0102);
+		ibmcam_veio(uvd, 0, 0x002c, 0x0103);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0104);
+		ibmcam_veio(uvd, 0, 0x0024, 0x0105);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0016, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0006, 0x0127);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+		ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0014, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0002, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012e);
+		ibmcam_veio(uvd, 0, 0x001a, 0x0130);
+		ibmcam_veio(uvd, 0, 0x8a0a, 0x0124);
+		ibmcam_veio(uvd, 0, 0x005e, 0x012d);
+		ibmcam_veio(uvd, 0, 0x9545, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x0127);
+		ibmcam_veio(uvd, 0, 0x0018, 0x012e);
+		ibmcam_veio(uvd, 0, 0x0049, 0x0130);
+		ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd055, 0x0124);
+		ibmcam_veio(uvd, 0, 0x001c, 0x0127);
+		ibmcam_veio(uvd, 0, 0x00cf, 0x012e);
+		ibmcam_veio(uvd, 0, 0xaa28, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0032, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+		ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0036, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0008, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+		ibmcam_veio(uvd, 0, 0xfffa, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x001e, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0010, 0x0127);
+		ibmcam_veio(uvd, 0, 0x0013, 0x012e);
+		ibmcam_veio(uvd, 0, 0x0025, 0x0130);
+		ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0010, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0048, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+		ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+		ibmcam_veio(uvd, 0, 0x0038, 0x012f);
+		ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+		ibmcam_veio(uvd, 0, 0x0004, 0x0127);
+		ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+		ibmcam_veio(uvd, 0, 0x00c0, 0x010c);
+		break; 
 	}
+	usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp));
 }
 
-/*
- * ibmcam_init_isoc()
- *
- * History:
- * 1/27/00  Used ibmcam->iface, ibmcam->ifaceAltActive instead of hardcoded values.
- *          Simplified by using for loop, allowed any number of URBs.
- */
-static int ibmcam_init_isoc(struct usb_ibmcam *ibmcam)
+static void ibmcam_model3_setup_after_video_if(uvd_t *uvd)
 {
-	struct usb_device *dev = ibmcam->dev;
-	int i, err;
-
-	if (!IBMCAM_IS_OPERATIONAL(ibmcam))
-		return -EFAULT;
-
-	ibmcam->compress = 0;
-	ibmcam->curframe = -1;
-	ibmcam->cursbuf = 0;
-	ibmcam->scratchlen = 0;
-
-	/* Alternate interface 1 is is the biggest frame size */
-	i = usb_set_interface(dev, ibmcam->iface, ibmcam->ifaceAltActive);
-	if (i < 0) {
-		printk(KERN_ERR "usb_set_interface error\n");
-		ibmcam->last_error = i;
-		return -EBUSY;
-	}
-	usb_ibmcam_change_lighting_conditions(ibmcam);
-	usb_ibmcam_set_sharpness(ibmcam);
-	usb_ibmcam_reinit_iso(ibmcam, 0);
-
-	/* We double buffer the Iso lists */
-
-	for (i=0; i < IBMCAM_NUMSBUF; i++) {
-		int j, k;
-		urb_t *urb;
-
-		urb = usb_alloc_urb(FRAMES_PER_DESC);
-		if (urb == NULL) {
-			printk(KERN_ERR "ibmcam_init_isoc: usb_init_isoc() failed.\n");
-			return -ENOMEM;
-		}
-		ibmcam->sbuf[i].urb = urb;
-		urb->dev = dev;
-		urb->context = ibmcam;
-		urb->pipe = usb_rcvisocpipe(dev, ibmcam->video_endp);
-		urb->transfer_flags = USB_ISO_ASAP;
-		urb->transfer_buffer = ibmcam->sbuf[i].data;
-		urb->complete = ibmcam_isoc_irq;
-		urb->number_of_packets = FRAMES_PER_DESC;
-		urb->transfer_buffer_length = ibmcam->iso_packet_len * FRAMES_PER_DESC;
-		for (j=k=0; j < FRAMES_PER_DESC; j++, k += ibmcam->iso_packet_len) {
-			urb->iso_frame_desc[j].offset = k;
-			urb->iso_frame_desc[j].length = ibmcam->iso_packet_len;
-		}
-	}
-
-	/* Link URBs into a ring so that they invoke each other infinitely */
-	for (i=0; i < IBMCAM_NUMSBUF; i++) {
-		if ((i+1) < IBMCAM_NUMSBUF)
-			ibmcam->sbuf[i].urb->next = ibmcam->sbuf[i+1].urb;
-		else
-			ibmcam->sbuf[i].urb->next = ibmcam->sbuf[0].urb;
-	}
-
-	/* Submit all URBs */
-	for (i=0; i < IBMCAM_NUMSBUF; i++) {
-		err = usb_submit_urb(ibmcam->sbuf[i].urb);
-		if (err)
-			printk(KERN_ERR "ibmcam_init_isoc: usb_run_isoc(%d) ret %d\n",
-			       i, err);
-	}
-
-	ibmcam->streaming = 1;
-	/* printk(KERN_DEBUG "streaming=1 ibmcam->video_endp=$%02x\n", ibmcam->video_endp); */
-	return 0;
-}
+	int i;
+	/*
+	 * 01.01.08 - Added for RCA video in support -LO
+	 * This struct is used to init the Model3 cam to use the RCA video in port
+	 * instead of the CCD sensor.
+	 */
+	static const struct struct_initData initData[] = {
+		{0, 0x0000, 0x010c},
+		{0, 0x0006, 0x012c},
+		{0, 0x0078, 0x012d},
+		{0, 0x0046, 0x012f},
+		{0, 0xd141, 0x0124},
+		{0, 0x0000, 0x0127},
+		{0, 0xfea8, 0x0124},
+		{1, 0x0000, 0x0116},
+		{0, 0x0064, 0x0116},
+		{1, 0x0000, 0x0115},
+		{0, 0x0003, 0x0115},
+		{0, 0x0008, 0x0123},
+		{0, 0x0000, 0x0117},
+		{0, 0x0000, 0x0112},
+		{0, 0x0080, 0x0100},
+		{0, 0x0000, 0x0100},
+		{1, 0x0000, 0x0116},
+		{0, 0x0060, 0x0116},
+		{0, 0x0002, 0x0112},
+		{0, 0x0000, 0x0123},
+		{0, 0x0001, 0x0117},
+		{0, 0x0040, 0x0108},
+		{0, 0x0019, 0x012c},
+		{0, 0x0040, 0x0116},
+		{0, 0x000a, 0x0115},
+		{0, 0x000b, 0x0115},
+		{0, 0x0078, 0x012d},
+		{0, 0x0046, 0x012f},
+		{0, 0xd141, 0x0124},
+		{0, 0x0000, 0x0127},
+		{0, 0xfea8, 0x0124},
+		{0, 0x0064, 0x0116},
+		{0, 0x0000, 0x0115},
+		{0, 0x0001, 0x0115},
+		{0, 0xffff, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x00aa, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0000, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xffff, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x00f2, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x000f, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xffff, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x00f8, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x00fc, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xffff, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x00f9, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x003c, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xffff, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0027, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0019, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0037, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0000, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0021, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0038, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0006, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0045, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0037, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0001, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x002a, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0038, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0000, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x000e, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0037, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0001, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x002b, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0038, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0001, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x00f4, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0037, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0001, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x002c, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0038, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0001, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0004, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0037, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0001, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x002d, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0038, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0000, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0014, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0037, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0001, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x002e, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0038, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0003, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0000, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0037, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0001, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x002f, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0038, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0003, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0014, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0037, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0001, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0040, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0038, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0000, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0040, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0037, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0001, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0053, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0038, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0000, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0038, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0x0000, 0x0101},
+		{0, 0x00a0, 0x0103},
+		{0, 0x0078, 0x0105},
+		{0, 0x0000, 0x010a},
+		{0, 0x0024, 0x010b},
+		{0, 0x0028, 0x0119},
+		{0, 0x0088, 0x011b},
+		{0, 0x0002, 0x011d},
+		{0, 0x0003, 0x011e},
+		{0, 0x0000, 0x0129},
+		{0, 0x00fc, 0x012b},
+		{0, 0x0008, 0x0102},
+		{0, 0x0000, 0x0104},
+		{0, 0x0008, 0x011a},
+		{0, 0x0028, 0x011c},
+		{0, 0x0021, 0x012a},
+		{0, 0x0000, 0x0118},
+		{0, 0x0000, 0x0132},
+		{0, 0x0000, 0x0109},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0037, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0001, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0031, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0038, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0000, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0000, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0037, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0001, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0040, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0038, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0000, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0040, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0037, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0000, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x00dc, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0038, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0000, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0000, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0037, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0001, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0032, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0038, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0001, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0020, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0037, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0001, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0040, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0038, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0000, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0040, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0037, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0000, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0030, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0xfff9, 0x0124},
+		{0, 0x0086, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0038, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0008, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0x0000, 0x0127},
+		{0, 0xfff8, 0x0124},
+		{0, 0xfffd, 0x0124},
+		{0, 0xfffa, 0x0124},
+		{0, 0x0003, 0x0106},
+		{0, 0x0062, 0x0107},
+		{0, 0x0003, 0x0111},
+	};
+#define NUM_INIT_DATA 
 
-/*
- * ibmcam_stop_isoc()
- *
- * This procedure stops streaming and deallocates URBs. Then it
- * activates zero-bandwidth alt. setting of the video interface.
- *
- * History:
- * 1/22/00  Corrected order of actions to work after surprise removal.
- * 1/27/00  Used ibmcam->iface, ibmcam->ifaceAltInactive instead of hardcoded values.
- */
-static void ibmcam_stop_isoc(struct usb_ibmcam *ibmcam)
-{
-	static const char proc[] = "ibmcam_stop_isoc";
-	int i, j;
+	unsigned short compression = 0;	/* 0=none, 7=best frame rate  */
+	int f_rate; /* 0=Fastest 7=slowest */
 
-	if (!ibmcam->streaming || (ibmcam->dev == NULL))
+	if (IBMCAM_T(uvd)->initialized)
 		return;
 
-	/* Unschedule all of the iso td's */
-	for (i=0; i < IBMCAM_NUMSBUF; i++) {
-		j = usb_unlink_urb(ibmcam->sbuf[i].urb);
-		if (j < 0)
-			printk(KERN_ERR "%s: usb_unlink_urb() error %d.\n", proc, j);
-	}
-	/* printk(KERN_DEBUG "streaming=0\n"); */
-	ibmcam->streaming = 0;
-
-	/* Delete them all */
-	for (i=0; i < IBMCAM_NUMSBUF; i++)
-		usb_free_urb(ibmcam->sbuf[i].urb);
-
-	if (!ibmcam->remove_pending) {
-		usb_ibmcam_setup_video_stop(ibmcam);
-
-		/* Set packet size to 0 */
-		j = usb_set_interface(ibmcam->dev, ibmcam->iface, ibmcam->ifaceAltInactive);
-		if (j < 0) {
-			printk(KERN_ERR "%s: usb_set_interface() error %d.\n", proc, j);
-			ibmcam->last_error = j;
-		}
-	}
-}
-
-/*
- * ibmcam_new_frame()
- *
- * History:
- * 29-Mar-00 Added copying of previous frame into the current one.
- */
-static int ibmcam_new_frame(struct usb_ibmcam *ibmcam, int framenum)
-{
-	struct ibmcam_frame *frame;
-	int n, width, height;
-
-	/* If we're not grabbing a frame right now and the other frame is */
-	/*  ready to be grabbed into, then use it instead */
-	if (ibmcam->curframe != -1)
-		return 0;
-
-	n = (framenum - 1 + IBMCAM_NUMFRAMES) % IBMCAM_NUMFRAMES;
-	if (ibmcam->frame[n].grabstate == FRAME_READY)
-		framenum = n;
-
-	frame = &ibmcam->frame[framenum];
-
-	frame->grabstate = FRAME_GRABBING;
-	frame->scanstate = STATE_SCANNING;
-	frame->scanlength = 0;		/* Accumulated in ibmcam_parse_data() */
-	ibmcam->curframe = framenum;
-
-	/*
-	 * Normally we would want to copy previous frame into the current one
-	 * before we even start filling it with data; this allows us to stop
-	 * filling at any moment; top portion of the frame will be new and
-	 * bottom portion will stay as it was in previous frame. If we don't
-	 * do that then missing chunks of video stream will result in flickering
-	 * portions of old data whatever it was before.
-	 *
-	 * If we choose not to copy previous frame (to, for example, save few
-	 * bus cycles - the frame can be pretty large!) then we have an option
-	 * to clear the frame before using. If we experience losses in this
-	 * mode then missing picture will be black (no flickering).
-	 *
-	 * Finally, if user chooses not to clean the current frame before
-	 * filling it with data then the old data will be visible if we fail
-	 * to refill entire frame with new data.
-	 */
-	if (!(flags & FLAGS_SEPARATE_FRAMES)) {
-		/* This copies previous frame into this one to mask losses */
-		memmove(frame->data, ibmcam->frame[1-framenum].data,  MAX_FRAME_SIZE);
-	} else {
-		if (flags & FLAGS_CLEAN_FRAMES) {
-			/* This provides a "clean" frame but slows things down */
-			memset(frame->data, 0, MAX_FRAME_SIZE);
-		}
-	}
-	switch (videosize) {
-	case VIDEOSIZE_128x96:
-		frame->frmwidth = 128;
-		frame->frmheight = 96;
-		frame->order_uv = 1;	/* U Y V Y ... */
-		frame->hdr_sig = 0x06;	/* 00 FF 00 06 */
+	/* Internal frame rate is controlled by f_rate value */
+	f_rate = 7 - framerate;
+	RESTRICT_TO_RANGE(f_rate, 0, 7);
+
+	ibmcam_veio(uvd, 0, 0x0000, 0x0100);
+	ibmcam_veio(uvd, 1, 0x0000, 0x0116);
+	ibmcam_veio(uvd, 0, 0x0060, 0x0116);
+	ibmcam_veio(uvd, 0, 0x0002, 0x0112);
+	ibmcam_veio(uvd, 0, 0x0000, 0x0123);
+	ibmcam_veio(uvd, 0, 0x0001, 0x0117);
+	ibmcam_veio(uvd, 0, 0x0040, 0x0108);
+	ibmcam_veio(uvd, 0, 0x0019, 0x012c);
+	ibmcam_veio(uvd, 0, 0x0060, 0x0116);
+	ibmcam_veio(uvd, 0, 0x0002, 0x0115);
+	ibmcam_veio(uvd, 0, 0x0003, 0x0115);
+	ibmcam_veio(uvd, 1, 0x0000, 0x0115);
+	ibmcam_veio(uvd, 0, 0x000b, 0x0115);
+	ibmcam_model3_Packet1(uvd, 0x000a, 0x0040);
+	ibmcam_model3_Packet1(uvd, 0x000b, 0x00f6);
+	ibmcam_model3_Packet1(uvd, 0x000c, 0x0002);
+	ibmcam_model3_Packet1(uvd, 0x000d, 0x0020);
+	ibmcam_model3_Packet1(uvd, 0x000e, 0x0033);
+	ibmcam_model3_Packet1(uvd, 0x000f, 0x0007);
+	ibmcam_model3_Packet1(uvd, 0x0010, 0x0000);
+	ibmcam_model3_Packet1(uvd, 0x0011, 0x0070);
+	ibmcam_model3_Packet1(uvd, 0x0012, 0x0030);
+	ibmcam_model3_Packet1(uvd, 0x0013, 0x0000);
+	ibmcam_model3_Packet1(uvd, 0x0014, 0x0001);
+	ibmcam_model3_Packet1(uvd, 0x0015, 0x0001);
+	ibmcam_model3_Packet1(uvd, 0x0016, 0x0001);
+	ibmcam_model3_Packet1(uvd, 0x0017, 0x0001);
+	ibmcam_model3_Packet1(uvd, 0x0018, 0x0000);
+	ibmcam_model3_Packet1(uvd, 0x001e, 0x00c3);
+	ibmcam_model3_Packet1(uvd, 0x0020, 0x0000);
+	ibmcam_model3_Packet1(uvd, 0x0028, 0x0010);
+	ibmcam_model3_Packet1(uvd, 0x0029, 0x0054);
+	ibmcam_model3_Packet1(uvd, 0x002a, 0x0013);
+	ibmcam_model3_Packet1(uvd, 0x002b, 0x0007);
+	ibmcam_model3_Packet1(uvd, 0x002d, 0x0028);
+	ibmcam_model3_Packet1(uvd, 0x002e, 0x0000);
+	ibmcam_model3_Packet1(uvd, 0x0031, 0x0000);
+	ibmcam_model3_Packet1(uvd, 0x0032, 0x0000);
+	ibmcam_model3_Packet1(uvd, 0x0033, 0x0000);
+	ibmcam_model3_Packet1(uvd, 0x0034, 0x0000);
+	ibmcam_model3_Packet1(uvd, 0x0035, 0x0038);
+	ibmcam_model3_Packet1(uvd, 0x003a, 0x0001);
+	ibmcam_model3_Packet1(uvd, 0x003c, 0x001e);
+	ibmcam_model3_Packet1(uvd, 0x003f, 0x000a);
+	ibmcam_model3_Packet1(uvd, 0x0041, 0x0000);
+	ibmcam_model3_Packet1(uvd, 0x0046, 0x003f);
+	ibmcam_model3_Packet1(uvd, 0x0047, 0x0000);
+	ibmcam_model3_Packet1(uvd, 0x0050, 0x0005);
+	ibmcam_model3_Packet1(uvd, 0x0052, 0x001a);
+	ibmcam_model3_Packet1(uvd, 0x0053, 0x0003);
+	ibmcam_model3_Packet1(uvd, 0x005a, 0x006b);
+	ibmcam_model3_Packet1(uvd, 0x005d, 0x001e);
+	ibmcam_model3_Packet1(uvd, 0x005e, 0x0030);
+	ibmcam_model3_Packet1(uvd, 0x005f, 0x0041);
+	ibmcam_model3_Packet1(uvd, 0x0064, 0x0008);
+	ibmcam_model3_Packet1(uvd, 0x0065, 0x0015);
+	ibmcam_model3_Packet1(uvd, 0x0068, 0x000f);
+	ibmcam_model3_Packet1(uvd, 0x0079, 0x0000);
+	ibmcam_model3_Packet1(uvd, 0x007a, 0x0000);
+	ibmcam_model3_Packet1(uvd, 0x007c, 0x003f);
+	ibmcam_model3_Packet1(uvd, 0x0082, 0x000f);
+	ibmcam_model3_Packet1(uvd, 0x0085, 0x0000);
+	ibmcam_model3_Packet1(uvd, 0x0099, 0x0000);
+	ibmcam_model3_Packet1(uvd, 0x009b, 0x0023);
+	ibmcam_model3_Packet1(uvd, 0x009c, 0x0022);
+	ibmcam_model3_Packet1(uvd, 0x009d, 0x0096);
+	ibmcam_model3_Packet1(uvd, 0x009e, 0x0096);
+	ibmcam_model3_Packet1(uvd, 0x009f, 0x000a);
+
+	switch (uvd->videosize) {
+	case VIDEOSIZE_160x120:
+		ibmcam_veio(uvd, 0, 0x0000, 0x0101); /* Same on 176x144, 320x240 */
+		ibmcam_veio(uvd, 0, 0x00a0, 0x0103); /* Same on 176x144, 320x240 */
+		ibmcam_veio(uvd, 0, 0x0078, 0x0105); /* Same on 176x144, 320x240 */
+		ibmcam_veio(uvd, 0, 0x0000, 0x010a); /* Same */
+		ibmcam_veio(uvd, 0, 0x0024, 0x010b); /* Differs everywhere */
+		ibmcam_veio(uvd, 0, 0x00a9, 0x0119);
+		ibmcam_veio(uvd, 0, 0x0016, 0x011b);
+		ibmcam_veio(uvd, 0, 0x0002, 0x011d); /* Same on 176x144, 320x240 */
+		ibmcam_veio(uvd, 0, 0x0003, 0x011e); /* Same on 176x144, 640x480 */
+		ibmcam_veio(uvd, 0, 0x0000, 0x0129); /* Same */
+		ibmcam_veio(uvd, 0, 0x00fc, 0x012b); /* Same */
+		ibmcam_veio(uvd, 0, 0x0018, 0x0102);
+		ibmcam_veio(uvd, 0, 0x0004, 0x0104);
+		ibmcam_veio(uvd, 0, 0x0004, 0x011a);
+		ibmcam_veio(uvd, 0, 0x0028, 0x011c);
+		ibmcam_veio(uvd, 0, 0x0022, 0x012a); /* Same */
+		ibmcam_veio(uvd, 0, 0x0000, 0x0118);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0132);
+		ibmcam_model3_Packet1(uvd, 0x0021, 0x0001); /* Same */
+		ibmcam_veio(uvd, 0, compression, 0x0109);
 		break;
-	case VIDEOSIZE_176x144:
-		frame->frmwidth = 176;
-		frame->frmheight = 144;
-		frame->order_uv = 1;	/* U Y V Y ... */
-		frame->hdr_sig = 0x0E;	/* 00 FF 00 0E */
-		break;
-	case VIDEOSIZE_320x240:		/* For model 2 only */
-		frame->frmwidth = 320;
-		frame->frmheight = 240;
-		break;
-	case VIDEOSIZE_352x240:		/* For model 2 only */
-		frame->frmwidth = 352;
-		frame->frmheight = 240;
+	case VIDEOSIZE_320x240:
+		ibmcam_veio(uvd, 0, 0x0000, 0x0101); /* Same on 176x144, 320x240 */
+		ibmcam_veio(uvd, 0, 0x00a0, 0x0103); /* Same on 176x144, 320x240 */
+		ibmcam_veio(uvd, 0, 0x0078, 0x0105); /* Same on 176x144, 320x240 */
+		ibmcam_veio(uvd, 0, 0x0000, 0x010a); /* Same */
+		ibmcam_veio(uvd, 0, 0x0028, 0x010b); /* Differs everywhere */
+		ibmcam_veio(uvd, 0, 0x0002, 0x011d); /* Same */
+		ibmcam_veio(uvd, 0, 0x0000, 0x011e);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0129); /* Same */
+		ibmcam_veio(uvd, 0, 0x00fc, 0x012b); /* Same */
+		/* 4 commands from 160x120 skipped */
+		ibmcam_veio(uvd, 0, 0x0022, 0x012a); /* Same */
+		ibmcam_model3_Packet1(uvd, 0x0021, 0x0001); /* Same */
+		ibmcam_veio(uvd, 0, compression, 0x0109);
+		ibmcam_veio(uvd, 0, 0x00d9, 0x0119);
+		ibmcam_veio(uvd, 0, 0x0006, 0x011b);
+		ibmcam_veio(uvd, 0, 0x0021, 0x0102); /* Same on 320x240, 640x480 */
+		ibmcam_veio(uvd, 0, 0x0010, 0x0104);
+		ibmcam_veio(uvd, 0, 0x0004, 0x011a);
+		ibmcam_veio(uvd, 0, 0x003f, 0x011c);
+		ibmcam_veio(uvd, 0, 0x001c, 0x0118);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0132);
+		break;
+	case VIDEOSIZE_640x480:
+		ibmcam_veio(uvd, 0, 0x00f0, 0x0105);
+		ibmcam_veio(uvd, 0, 0x0000, 0x010a); /* Same */
+		ibmcam_veio(uvd, 0, 0x0038, 0x010b); /* Differs everywhere */
+		ibmcam_veio(uvd, 0, 0x00d9, 0x0119); /* Same on 320x240, 640x480 */
+		ibmcam_veio(uvd, 0, 0x0006, 0x011b); /* Same on 320x240, 640x480 */
+		ibmcam_veio(uvd, 0, 0x0004, 0x011d); /* NC */
+		ibmcam_veio(uvd, 0, 0x0003, 0x011e); /* Same on 176x144, 640x480 */
+		ibmcam_veio(uvd, 0, 0x0000, 0x0129); /* Same */
+		ibmcam_veio(uvd, 0, 0x00fc, 0x012b); /* Same */
+		ibmcam_veio(uvd, 0, 0x0021, 0x0102); /* Same on 320x240, 640x480 */
+		ibmcam_veio(uvd, 0, 0x0016, 0x0104); /* NC */
+		ibmcam_veio(uvd, 0, 0x0004, 0x011a); /* Same on 320x240, 640x480 */
+		ibmcam_veio(uvd, 0, 0x003f, 0x011c); /* Same on 320x240, 640x480 */
+		ibmcam_veio(uvd, 0, 0x0022, 0x012a); /* Same */
+		ibmcam_veio(uvd, 0, 0x001c, 0x0118); /* Same on 320x240, 640x480 */
+		ibmcam_model3_Packet1(uvd, 0x0021, 0x0001); /* Same */
+		ibmcam_veio(uvd, 0, compression, 0x0109);
+		ibmcam_veio(uvd, 0, 0x0040, 0x0101);
+		ibmcam_veio(uvd, 0, 0x0040, 0x0103);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0132); /* Same on 320x240, 640x480 */
+		break;
+	}
+	ibmcam_model3_Packet1(uvd, 0x007e, 0x000e);	/* Hue */
+	ibmcam_model3_Packet1(uvd, 0x0036, 0x0011);	/* Brightness */
+	ibmcam_model3_Packet1(uvd, 0x0060, 0x0002);	/* Sharpness */
+	ibmcam_model3_Packet1(uvd, 0x0061, 0x0004);	/* Sharpness */
+	ibmcam_model3_Packet1(uvd, 0x0062, 0x0005);	/* Sharpness */
+	ibmcam_model3_Packet1(uvd, 0x0063, 0x0014);	/* Sharpness */
+	ibmcam_model3_Packet1(uvd, 0x0096, 0x00a0);	/* Red gain */
+	ibmcam_model3_Packet1(uvd, 0x0097, 0x0096);	/* Blue gain */
+	ibmcam_model3_Packet1(uvd, 0x0067, 0x0001);	/* Contrast */
+	ibmcam_model3_Packet1(uvd, 0x005b, 0x000c);	/* Contrast */
+	ibmcam_model3_Packet1(uvd, 0x005c, 0x0016);	/* Contrast */
+	ibmcam_model3_Packet1(uvd, 0x0098, 0x000b);
+	ibmcam_model3_Packet1(uvd, 0x002c, 0x0003);	/* Was 1, broke 640x480 */
+	ibmcam_model3_Packet1(uvd, 0x002f, 0x002a);
+	ibmcam_model3_Packet1(uvd, 0x0030, 0x0029);
+	ibmcam_model3_Packet1(uvd, 0x0037, 0x0002);
+	ibmcam_model3_Packet1(uvd, 0x0038, 0x0059);
+	ibmcam_model3_Packet1(uvd, 0x003d, 0x002e);
+	ibmcam_model3_Packet1(uvd, 0x003e, 0x0028);
+	ibmcam_model3_Packet1(uvd, 0x0078, 0x0005);
+	ibmcam_model3_Packet1(uvd, 0x007b, 0x0011);
+	ibmcam_model3_Packet1(uvd, 0x007d, 0x004b);
+	ibmcam_model3_Packet1(uvd, 0x007f, 0x0022);
+	ibmcam_model3_Packet1(uvd, 0x0080, 0x000c);
+	ibmcam_model3_Packet1(uvd, 0x0081, 0x000b);
+	ibmcam_model3_Packet1(uvd, 0x0083, 0x00fd);
+	ibmcam_model3_Packet1(uvd, 0x0086, 0x000b);
+	ibmcam_model3_Packet1(uvd, 0x0087, 0x000b);
+	ibmcam_model3_Packet1(uvd, 0x007e, 0x000e);
+	ibmcam_model3_Packet1(uvd, 0x0096, 0x00a0);	/* Red gain */
+	ibmcam_model3_Packet1(uvd, 0x0097, 0x0096);	/* Blue gain */
+	ibmcam_model3_Packet1(uvd, 0x0098, 0x000b);
+
+	switch (uvd->videosize) {
+	case VIDEOSIZE_160x120:
+		ibmcam_veio(uvd, 0, 0x0002, 0x0106);
+		ibmcam_veio(uvd, 0, 0x0008, 0x0107);
+		ibmcam_veio(uvd, 0, f_rate, 0x0111);	/* Frame rate */
+		ibmcam_model3_Packet1(uvd, 0x001f, 0x0000); /* Same */
+		ibmcam_model3_Packet1(uvd, 0x0039, 0x001f); /* Same */
+		ibmcam_model3_Packet1(uvd, 0x003b, 0x003c); /* Same */
+		ibmcam_model3_Packet1(uvd, 0x0040, 0x000a);
+		ibmcam_model3_Packet1(uvd, 0x0051, 0x000a);
 		break;
-	case VIDEOSIZE_352x288:
-		frame->frmwidth = 352;
-		frame->frmheight = 288;
-		frame->order_uv = 0;	/* V Y U Y ... */
-		frame->hdr_sig = 0x00;	/* 00 FF 00 00 */
+	case VIDEOSIZE_320x240:
+		ibmcam_veio(uvd, 0, 0x0003, 0x0106);
+		ibmcam_veio(uvd, 0, 0x0062, 0x0107);
+		ibmcam_veio(uvd, 0, f_rate, 0x0111);	/* Frame rate */
+		ibmcam_model3_Packet1(uvd, 0x001f, 0x0000); /* Same */
+		ibmcam_model3_Packet1(uvd, 0x0039, 0x001f); /* Same */
+		ibmcam_model3_Packet1(uvd, 0x003b, 0x003c); /* Same */
+		ibmcam_model3_Packet1(uvd, 0x0040, 0x0008);
+		ibmcam_model3_Packet1(uvd, 0x0051, 0x000b);
+		break;
+	case VIDEOSIZE_640x480:
+		ibmcam_veio(uvd, 0, 0x0002, 0x0106);	/* Adjustments */
+		ibmcam_veio(uvd, 0, 0x00b4, 0x0107);	/* Adjustments */
+		ibmcam_veio(uvd, 0, f_rate, 0x0111);	/* Frame rate */
+		ibmcam_model3_Packet1(uvd, 0x001f, 0x0002); /* !Same */
+		ibmcam_model3_Packet1(uvd, 0x0039, 0x003e); /* !Same */
+		ibmcam_model3_Packet1(uvd, 0x0040, 0x0008);
+		ibmcam_model3_Packet1(uvd, 0x0051, 0x000a);
 		break;
 	}
-	frame->order_yc = (ibmcam->camera_model == IBMCAM_MODEL_2);
-
-	width = frame->width;
-	RESTRICT_TO_RANGE(width, min_imgwidth, imgwidth);
-	width &= ~7;		/* Multiple of 8 */
-
-	height = frame->height;
-	RESTRICT_TO_RANGE(height, min_imgheight, imgheight);
-	height &= ~3;		/* Multiple of 4 */
-
-	return 0;
-}
-
-/*
- * ibmcam_open()
- *
- * This is part of Video 4 Linux API. The driver can be opened by one
- * client only (checks internal counter 'ibmcam->user'). The procedure
- * then allocates buffers needed for video processing.
- *
- * History:
- * 1/22/00  Rewrote, moved scratch buffer allocation here. Now the
- *          camera is also initialized here (once per connect), at
- *          expense of V4L client (it waits on open() call).
- * 1/27/00  Used IBMCAM_NUMSBUF as number of URB buffers.
- * 5/24/00  Corrected to prevent race condition (MOD_xxx_USE_COUNT).
- */
-static int ibmcam_open(struct video_device *dev, int flags)
-{
-	struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev;
-	const int sb_size = FRAMES_PER_DESC * ibmcam->iso_packet_len;
-	int i, err = 0;
-
-	MOD_INC_USE_COUNT;
-	down(&ibmcam->lock);
 
-	if (ibmcam->user)
-		err = -EBUSY;
-	else {
-		/* Clean pointers so we know if we allocated something */
-		for (i=0; i < IBMCAM_NUMSBUF; i++)
-			ibmcam->sbuf[i].data = NULL;
-
-		/* Allocate memory for the frame buffers */
-		ibmcam->fbuf_size = IBMCAM_NUMFRAMES * MAX_FRAME_SIZE;
-		ibmcam->fbuf = rvmalloc(ibmcam->fbuf_size);
-		ibmcam->scratch = kmalloc(scratchbufsize, GFP_KERNEL);
-		ibmcam->scratchlen = 0;
-		if ((ibmcam->fbuf == NULL) || (ibmcam->scratch == NULL))
-			err = -ENOMEM;
-		else {
-			/* Allocate all buffers */
-			for (i=0; i < IBMCAM_NUMFRAMES; i++) {
-				ibmcam->frame[i].grabstate = FRAME_UNUSED;
-				ibmcam->frame[i].data = ibmcam->fbuf + i*MAX_FRAME_SIZE;
-				/*
-				 * Set default sizes in case IOCTL (VIDIOCMCAPTURE)
-				 * is not used (using read() instead).
-				 */
-				ibmcam->frame[i].width = imgwidth;
-				ibmcam->frame[i].height = imgheight;
-				ibmcam->frame[i].bytes_read = 0;
-			}
-			for (i=0; i < IBMCAM_NUMSBUF; i++) {
-				ibmcam->sbuf[i].data = kmalloc(sb_size, GFP_KERNEL);
-				if (ibmcam->sbuf[i].data == NULL) {
-					err = -ENOMEM;
-					break;
-				}
-			}
-		}
-		if (err) {
-			/* Have to free all that memory */
-			if (ibmcam->fbuf != NULL) {
-				rvfree(ibmcam->fbuf, ibmcam->fbuf_size);
-				ibmcam->fbuf = NULL;
-			}
-			if (ibmcam->scratch != NULL) {
-				kfree(ibmcam->scratch);
-				ibmcam->scratch = NULL;
-			}
-			for (i=0; i < IBMCAM_NUMSBUF; i++) {
-				if (ibmcam->sbuf[i].data != NULL) {
-					kfree (ibmcam->sbuf[i].data);
-					ibmcam->sbuf[i].data = NULL;
-				}
-			}
+	/* 01.01.08 - Added for RCA video in support -LO */
+	if(init_model3_input) {
+		if (debug > 0)
+			info("Setting input to RCA.");
+		for (i=0; i < (sizeof(initData)/sizeof(initData[0])); i++) {
+			ibmcam_veio(uvd, initData[i].req, initData[i].value, initData[i].index);
 		}
 	}
 
-	/* If so far no errors then we shall start the camera */
-	if (!err) {
-		err = ibmcam_init_isoc(ibmcam);
-		if (!err) {
-			/* Send init sequence only once, it's large! */
-			if (!ibmcam->initialized) {
-				int setup_ok = 0;
-				if (ibmcam->camera_model == IBMCAM_MODEL_1)
-					setup_ok = usb_ibmcam_model1_setup(ibmcam);
-				else if (ibmcam->camera_model == IBMCAM_MODEL_2)
-					setup_ok = usb_ibmcam_model2_setup(ibmcam);
-				if (setup_ok)
-					ibmcam->initialized = 1;
-				else
-					err = -EBUSY;
-			}
-			if (!err)
-				ibmcam->user++;
-		}
-	}
-	up(&ibmcam->lock);
-	if (err)
-		MOD_DEC_USE_COUNT;
-	return err;
+	ibmcam_veio(uvd, 0, 0x0001, 0x0114);
+	ibmcam_veio(uvd, 0, 0x00c0, 0x010c);
+	usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp));
 }
 
 /*
- * ibmcam_close()
- *
- * This is part of Video 4 Linux API. The procedure
- * stops streaming and deallocates all buffers that were earlier
- * allocated in ibmcam_open().
+ * ibmcam_video_stop()
  *
- * History:
- * 1/22/00  Moved scratch buffer deallocation here.
- * 1/27/00  Used IBMCAM_NUMSBUF as number of URB buffers.
- * 5/24/00  Moved MOD_DEC_USE_COUNT outside of code that can sleep.
+ * This code tells camera to stop streaming. The interface remains
+ * configured and bandwidth - claimed.
  */
-static void ibmcam_close(struct video_device *dev)
+static void ibmcam_video_stop(uvd_t *uvd)
 {
-	struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev;
-	int i;
-
-	down(&ibmcam->lock);	
+	switch (IBMCAM_T(uvd)->camera_model) {
+	case IBMCAM_MODEL_1:
+		ibmcam_veio(uvd, 0, 0x00, 0x010c);
+		ibmcam_veio(uvd, 0, 0x00, 0x010c);
+		ibmcam_veio(uvd, 0, 0x01, 0x0114);
+		ibmcam_veio(uvd, 0, 0xc0, 0x010c);
+		ibmcam_veio(uvd, 0, 0x00, 0x010c);
+		ibmcam_send_FF_04_02(uvd);
+		ibmcam_veio(uvd, 1, 0x00, 0x0100);
+		ibmcam_veio(uvd, 0, 0x81, 0x0100);	/* LED Off */
+		break;
+	case IBMCAM_MODEL_2:
+case IBMCAM_MODEL_4:
+		ibmcam_veio(uvd, 0, 0x0000, 0x010c);	/* Stop the camera */
+
+		ibmcam_model2_Packet1(uvd, 0x0030, 0x0004);
+
+		ibmcam_veio(uvd, 0, 0x0080, 0x0100);	/* LED Off */
+		ibmcam_veio(uvd, 0, 0x0020, 0x0111);
+		ibmcam_veio(uvd, 0, 0x00a0, 0x0111);
 
-	ibmcam_stop_isoc(ibmcam);
+		ibmcam_model2_Packet1(uvd, 0x0030, 0x0002);
 
-	rvfree(ibmcam->fbuf, ibmcam->fbuf_size);
-	kfree(ibmcam->scratch);
-	for (i=0; i < IBMCAM_NUMSBUF; i++)
-		kfree(ibmcam->sbuf[i].data);
+		ibmcam_veio(uvd, 0, 0x0020, 0x0111);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0112);
+		break;
+	case IBMCAM_MODEL_3:
+#if 1
+		ibmcam_veio(uvd, 0, 0x0000, 0x010c);
 
-	ibmcam->user--;
+		/* Here we are supposed to select video interface alt. setting 0 */
+		ibmcam_veio(uvd, 0, 0x0006, 0x012c);
 
-	if (ibmcam->remove_pending) {
-		printk(KERN_INFO "ibmcam_close: Final disconnect.\n");
-		usb_ibmcam_release(ibmcam);
-	}
-	up(&ibmcam->lock);
-	MOD_DEC_USE_COUNT;
-}
+		ibmcam_model3_Packet1(uvd, 0x0046, 0x0000);
 
-static long ibmcam_write(struct video_device *dev, const char *buf, unsigned long count, int noblock)
-{
-	return -EINVAL;
+		ibmcam_veio(uvd, 1, 0x0000, 0x0116);
+		ibmcam_veio(uvd, 0, 0x0064, 0x0116);
+		ibmcam_veio(uvd, 1, 0x0000, 0x0115);
+		ibmcam_veio(uvd, 0, 0x0003, 0x0115);
+		ibmcam_veio(uvd, 0, 0x0008, 0x0123);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0117);
+		ibmcam_veio(uvd, 0, 0x0000, 0x0112);
+		ibmcam_veio(uvd, 0, 0x0080, 0x0100);
+		IBMCAM_T(uvd)->initialized = 0;
+#endif
+		break;
+	} /* switch */
 }
 
 /*
- * ibmcam_ioctl()
+ * ibmcam_reinit_iso()
  *
- * This is part of Video 4 Linux API. The procedure handles ioctl() calls.
+ * 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.
  *
  * History:
- * 1/22/00  Corrected VIDIOCSPICT to reject unsupported settings.
+ * 1/2/00   Created.
  */
-static int ibmcam_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+static void ibmcam_reinit_iso(uvd_t *uvd, int do_stop)
 {
-	struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev;
-
-	if (!IBMCAM_IS_OPERATIONAL(ibmcam))
-		return -EFAULT;
-
-	switch (cmd) {
-		case VIDIOCGCAP:
-		{
-			if (copy_to_user(arg, &ibmcam->vcap, sizeof(ibmcam->vcap)))
-				return -EFAULT;
-			return 0;
-		}
-		case VIDIOCGCHAN:
-		{
-			if (copy_to_user(arg, &ibmcam->vchan, sizeof(ibmcam->vchan)))
-				return -EFAULT;
-			return 0;
-		}
-		case VIDIOCSCHAN:
-		{
-			int v;
-
-			if (copy_from_user(&v, arg, sizeof(v)))
-				return -EFAULT;
-			if ((v < 0) || (v >= 3)) /* 3 grades of lighting conditions */
-				return -EINVAL;
-			if (v != ibmcam->vchan.channel) {
-				ibmcam->vchan.channel = v;
-				usb_ibmcam_change_lighting_conditions(ibmcam);
-			}
-			return 0;
-		}
-		case VIDIOCGPICT:
-		{
-			if (copy_to_user(arg, &ibmcam->vpic, sizeof(ibmcam->vpic)))
-				return -EFAULT;
-			return 0;
-		}
-		case VIDIOCSPICT:
-		{
-			struct video_picture tmp;
-			/*
-			 * Use temporary 'video_picture' structure to preserve our
-			 * own settings (such as color depth, palette) that we
-			 * aren't allowing everyone (V4L client) to change.
-			 */
-			if (copy_from_user(&tmp, arg, sizeof(tmp)))
-				return -EFAULT;
-			ibmcam->vpic.brightness = tmp.brightness;
-			ibmcam->vpic.hue = tmp.hue;
-			ibmcam->vpic.colour = tmp.colour;
-			ibmcam->vpic.contrast = tmp.contrast;
-			usb_ibmcam_adjust_picture(ibmcam);
-			return 0;
-		}
-		case VIDIOCSWIN:
-		{
-			struct video_window vw;
-
-			if (copy_from_user(&vw, arg, sizeof(vw)))
-				return -EFAULT;
-			if (vw.flags)
-				return -EINVAL;
-			if (vw.clipcount)
-				return -EINVAL;
-			if (vw.height != imgheight)
-				return -EINVAL;
-			if (vw.width != imgwidth)
-				return -EINVAL;
-
-			ibmcam->compress = 0;
-
-			return 0;
-		}
-		case VIDIOCGWIN:
-		{
-			struct video_window vw;
-
-			memset(&vw, 0, sizeof(vw));
-			vw.x = 0;
-			vw.y = 0;
-			vw.width = imgwidth;
-			vw.height = imgheight;
-			vw.flags = usb_ibmcam_calculate_fps();
-
-			if (copy_to_user(arg, &vw, sizeof(vw)))
-				return -EFAULT;
-
-			return 0;
-		}
-		case VIDIOCGMBUF:
-		{
-			struct video_mbuf vm;
-
-			memset(&vm, 0, sizeof(vm));
-			vm.size = MAX_FRAME_SIZE * 2;
-			vm.frames = 2;
-			vm.offsets[0] = 0;
-			vm.offsets[1] = MAX_FRAME_SIZE;
-
-			if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
-				return -EFAULT;
-
-			return 0;
-		}
-		case VIDIOCMCAPTURE:
-		{
-			struct video_mmap vm;
-
-			if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm)))
-				return -EFAULT;
-
-			if (debug >= 1)
-				printk(KERN_DEBUG "frame: %d, size: %dx%d, format: %d\n",
-					vm.frame, vm.width, vm.height, vm.format);
-
-			if (vm.format != VIDEO_PALETTE_RGB24)
-				return -EINVAL;
-
-			if ((vm.frame != 0) && (vm.frame != 1))
-				return -EINVAL;
-
-			if (ibmcam->frame[vm.frame].grabstate == FRAME_GRABBING)
-				return -EBUSY;
-
-			/* Don't compress if the size changed */
-			if ((ibmcam->frame[vm.frame].width != vm.width) ||
-			    (ibmcam->frame[vm.frame].height != vm.height))
-				ibmcam->compress = 0;
-
-			ibmcam->frame[vm.frame].width = vm.width;
-			ibmcam->frame[vm.frame].height = vm.height;
-
-			/* Mark it as ready */
-			ibmcam->frame[vm.frame].grabstate = FRAME_READY;
-
-			return ibmcam_new_frame(ibmcam, vm.frame);
-		}
-		case VIDIOCSYNC:
-		{
-			int frame;
-
-			if (copy_from_user((void *)&frame, arg, sizeof(int)))
-				return -EFAULT;
-
-			if ((unsigned)frame >= IBMCAM_NUMFRAMES) {
-				err("VIDIOCSYNC: invalid frame %d.", frame);
-				return -EINVAL;
-			}
-
-			if (debug >= 1)
-				printk(KERN_DEBUG "ibmcam: syncing to frame %d\n", frame);
-
-			switch (ibmcam->frame[frame].grabstate) {
-			case FRAME_UNUSED:
-				return -EINVAL;
-			case FRAME_READY:
-			case FRAME_GRABBING:
-			case FRAME_ERROR:
-			{
-				int ntries;
-		redo:
-				if (!IBMCAM_IS_OPERATIONAL(ibmcam))
-					return -EIO;
-				ntries = 0; 
-				do {
-					interruptible_sleep_on(&ibmcam->frame[frame].wq);
-					if (signal_pending(current)) {
-						if (flags & FLAGS_RETRY_VIDIOCSYNC) {
-							/* Polling apps will destroy frames with that! */
-							ibmcam_new_frame(ibmcam, frame);
-							usb_ibmcam_testpattern(ibmcam, 1, 0);
-							ibmcam->curframe = -1;
-							ibmcam->frame_num++;
-
-							/* This will request another frame. */
-							if (waitqueue_active(&ibmcam->frame[frame].wq))
-								wake_up_interruptible(&ibmcam->frame[frame].wq);
-							return 0;
-						} else {
-							/* Standard answer: not ready yet! */
-							return -EINTR;
-						}
-					}
-				} while (ibmcam->frame[frame].grabstate == FRAME_GRABBING);
-
-				if (ibmcam->frame[frame].grabstate == FRAME_ERROR) {
-					int ret = ibmcam_new_frame(ibmcam, frame);
-					if (ret < 0)
-						return ret;
-					goto redo;
-				}
-			}
-			case FRAME_DONE:
-				ibmcam->frame[frame].grabstate = FRAME_UNUSED;
-				break;
-			}
-
-			ibmcam->frame[frame].grabstate = FRAME_UNUSED;
-
-			return 0;
-		}
-		case VIDIOCGFBUF:
-		{
-			struct video_buffer vb;
-
-			memset(&vb, 0, sizeof(vb));
-			vb.base = NULL;	/* frame buffer not supported, not used */
-
-			if (copy_to_user((void *)arg, (void *)&vb, sizeof(vb)))
-				return -EFAULT;
-
- 			return 0;
- 		}
-		case VIDIOCKEY:
-			return 0;
-
-		case VIDIOCCAPTURE:
-			return -EINVAL;
-
-		case VIDIOCSFBUF:
-
-		case VIDIOCGTUNER:
-		case VIDIOCSTUNER:
-
-		case VIDIOCGFREQ:
-		case VIDIOCSFREQ:
-
-		case VIDIOCGAUDIO:
-		case VIDIOCSAUDIO:
-			return -EINVAL;
-
-		default:
-			return -ENOIOCTLCMD;
+	switch (IBMCAM_T(uvd)->camera_model) {
+	case IBMCAM_MODEL_1:
+		if (do_stop)
+			ibmcam_video_stop(uvd);
+		ibmcam_veio(uvd, 0, 0x0001, 0x0114);
+		ibmcam_veio(uvd, 0, 0x00c0, 0x010c);
+		usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp));
+		ibmcam_model1_setup_after_video_if(uvd);
+		break;
+	case IBMCAM_MODEL_2:
+		ibmcam_model2_setup_after_video_if(uvd);
+		break;
+	case IBMCAM_MODEL_3:
+		ibmcam_video_stop(uvd);
+		ibmcam_model3_setup_after_video_if(uvd);
+		break;
+	case IBMCAM_MODEL_4:
+		ibmcam_model4_setup_after_video_if(uvd);
+		break;
 	}
-	return 0;
 }
 
-static long ibmcam_read(struct video_device *dev, char *buf, unsigned long count, int noblock)
+static void ibmcam_video_start(uvd_t *uvd)
 {
-	struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev;
-	int frmx = -1;
-	volatile struct ibmcam_frame *frame;
-
-	if (debug >= 1)
-		printk(KERN_DEBUG "ibmcam_read: %ld bytes, noblock=%d\n", count, noblock);
-
-	if (!IBMCAM_IS_OPERATIONAL(ibmcam) || (buf == NULL))
-		return -EFAULT;
-
-	/* See if a frame is completed, then use it. */
-	if (ibmcam->frame[0].grabstate >= FRAME_DONE)	/* _DONE or _ERROR */
-		frmx = 0;
-	else if (ibmcam->frame[1].grabstate >= FRAME_DONE)/* _DONE or _ERROR */
-		frmx = 1;
-
-	if (noblock && (frmx == -1))
-		return -EAGAIN;
-
-	/* If no FRAME_DONE, look for a FRAME_GRABBING state. */
-	/* See if a frame is in process (grabbing), then use it. */
-	if (frmx == -1) {
-		if (ibmcam->frame[0].grabstate == FRAME_GRABBING)
-			frmx = 0;
-		else if (ibmcam->frame[1].grabstate == FRAME_GRABBING)
-			frmx = 1;
-	}
-
-	/* If no frame is active, start one. */
-	if (frmx == -1)
-		ibmcam_new_frame(ibmcam, frmx = 0);
-
-	frame = &ibmcam->frame[frmx];
-
-restart:
-	if (!IBMCAM_IS_OPERATIONAL(ibmcam))
-		return -EIO;
-	while (frame->grabstate == FRAME_GRABBING) {
-		interruptible_sleep_on((void *)&frame->wq);
-		if (signal_pending(current))
-			return -EINTR;
-	}
-
-	if (frame->grabstate == FRAME_ERROR) {
-		frame->bytes_read = 0;
-		if (ibmcam_new_frame(ibmcam, frmx))
-			printk(KERN_ERR "ibmcam_read: ibmcam_new_frame error\n");
-		goto restart;
-	}
-
-	if (debug >= 1)
-		printk(KERN_DEBUG "ibmcam_read: frmx=%d, bytes_read=%ld, scanlength=%ld\n",
-			frmx, frame->bytes_read, frame->scanlength);
-
-	/* copy bytes to user space; we allow for partials reads */
-	if ((count + frame->bytes_read) > frame->scanlength)
-		count = frame->scanlength - frame->bytes_read;
-
-	if (copy_to_user(buf, frame->data + frame->bytes_read, count))
-		return -EFAULT;
-
-	frame->bytes_read += count;
-	if (debug >= 1)
-		printk(KERN_DEBUG "ibmcam_read: {copy} count used=%ld, new bytes_read=%ld\n",
-			count, frame->bytes_read);
-
-	if (frame->bytes_read >= frame->scanlength) { /* All data has been read */
-		frame->bytes_read = 0;
-
-		/* Mark it as available to be used again. */
-		ibmcam->frame[frmx].grabstate = FRAME_UNUSED;
-		if (ibmcam_new_frame(ibmcam, frmx ? 0 : 1))
-			printk(KERN_ERR "ibmcam_read: ibmcam_new_frame returned error\n");
-	}
-
-	return count;
+	ibmcam_change_lighting_conditions(uvd);
+	ibmcam_set_sharpness(uvd);
+	ibmcam_reinit_iso(uvd, 0);
 }
 
-static int ibmcam_mmap(struct video_device *dev, const char *adr, unsigned long size)
+/*
+ * Return negative code on failure, 0 on success.
+ */
+static int ibmcam_setup_on_open(uvd_t *uvd)
 {
-	struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev;
-	unsigned long start = (unsigned long)adr;
-	unsigned long page, pos;
-
-	if (!IBMCAM_IS_OPERATIONAL(ibmcam))
-		return -EFAULT;
-
-	if (size > (((2 * MAX_FRAME_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))
-		return -EINVAL;
-
-	pos = (unsigned long)ibmcam->fbuf;
-	while (size > 0) {
-		page = kvirt_to_pa(pos);
-		if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED))
-			return -EAGAIN;
-
-		start += PAGE_SIZE;
-		pos += PAGE_SIZE;
-		if (size > PAGE_SIZE)
-			size -= PAGE_SIZE;
-		else
-			size = 0;
+	int setup_ok = 0; /* Success by default */
+	/* Send init sequence only once, it's large! */
+	if (!IBMCAM_T(uvd)->initialized) { /* FIXME rename */
+		switch (IBMCAM_T(uvd)->camera_model) {
+		case IBMCAM_MODEL_1:
+			setup_ok = ibmcam_model1_setup(uvd);
+			break;
+		case IBMCAM_MODEL_2:
+			setup_ok = ibmcam_model2_setup(uvd);
+			break;
+		case IBMCAM_MODEL_3:
+		case IBMCAM_MODEL_4:
+			/* We do all setup when Isoc stream is requested */
+			break;
+		}
+		IBMCAM_T(uvd)->initialized = (setup_ok != 0);
 	}
-
-	return 0;
+	return setup_ok;
 }
 
-static struct video_device ibmcam_template = {
-	name:		"CPiA USB Camera",
-	type:		VID_TYPE_CAPTURE,
-	hardware:	VID_HARDWARE_CPIA,
-	open:		ibmcam_open,
-	close:		ibmcam_close,
-	read:		ibmcam_read,
-	write:		ibmcam_write,
-	ioctl:		ibmcam_ioctl,
-	mmap:		ibmcam_mmap,
-};
-
-static void usb_ibmcam_configure_video(struct usb_ibmcam *ibmcam)
+static void ibmcam_configure_video(uvd_t *uvd)
 {
-	if (ibmcam == NULL)
+	if (uvd == NULL)
 		return;
 
 	RESTRICT_TO_RANGE(init_brightness, 0, 255);
@@ -2883,286 +3610,316 @@
 	RESTRICT_TO_RANGE(init_hue, 0, 255);
 	RESTRICT_TO_RANGE(hue_correction, 0, 255);
 
-	memset(&ibmcam->vpic, 0, sizeof(ibmcam->vpic));
-	memset(&ibmcam->vpic_old, 0x55, sizeof(ibmcam->vpic_old));
-
-	ibmcam->vpic.colour = init_color << 8;
-	ibmcam->vpic.hue = init_hue << 8;
-	ibmcam->vpic.brightness = init_brightness << 8;
-	ibmcam->vpic.contrast = init_contrast << 8;
-	ibmcam->vpic.whiteness = 105 << 8; /* This one isn't used */
-	ibmcam->vpic.depth = 24;
-	ibmcam->vpic.palette = VIDEO_PALETTE_RGB24;
-
-	memset(&ibmcam->vcap, 0, sizeof(ibmcam->vcap));
-	strcpy(ibmcam->vcap.name, "IBM USB Camera");
-	ibmcam->vcap.type = VID_TYPE_CAPTURE;
-	ibmcam->vcap.channels = 1;
-	ibmcam->vcap.audios = 0;
-	ibmcam->vcap.maxwidth = imgwidth;
-	ibmcam->vcap.maxheight = imgheight;
-	ibmcam->vcap.minwidth = min_imgwidth;
-	ibmcam->vcap.minheight = min_imgheight;
-
-	memset(&ibmcam->vchan, 0, sizeof(ibmcam->vchan));
-	ibmcam->vchan.flags = 0;
-	ibmcam->vchan.tuners = 0;
-	ibmcam->vchan.channel = 0;
-	ibmcam->vchan.type = VIDEO_TYPE_CAMERA;
-	strcpy(ibmcam->vchan.name, "Camera");
-}
+	memset(&uvd->vpic, 0, sizeof(uvd->vpic));
+	memset(&uvd->vpic_old, 0x55, sizeof(uvd->vpic_old));
 
-/*
- * ibmcam_find_struct()
- *
- * This code searches the array of preallocated (static) structures
- * and returns index of the first one that isn't in use. Returns -1
- * if there are no free structures.
- *
- * History:
- * 1/27/00  Created.
- */
-static int ibmcam_find_struct(void)
-{
-	int i, u;
-
-	for (u = 0; u < MAX_IBMCAM; u++) {
-		struct usb_ibmcam *ibmcam = &cams[u];
-		if (!ibmcam->ibmcam_used) /* This one is free */
-		{
-			ibmcam->ibmcam_used = 1;	/* In use now */
-			for (i=0; i < IBMCAM_NUMFRAMES; i++)
-				init_waitqueue_head(&ibmcam->frame[i].wq);
-			init_MUTEX(&ibmcam->lock);	/* to 1 == available */
-			ibmcam->dev = NULL;
-			memcpy(&ibmcam->vdev, &ibmcam_template, sizeof(ibmcam_template));
-			return u;
-		}
-	}
-	return -1;
+	uvd->vpic.colour = init_color << 8;
+	uvd->vpic.hue = init_hue << 8;
+	uvd->vpic.brightness = init_brightness << 8;
+	uvd->vpic.contrast = init_contrast << 8;
+	uvd->vpic.whiteness = 105 << 8; /* This one isn't used */
+	uvd->vpic.depth = 24;
+	uvd->vpic.palette = VIDEO_PALETTE_RGB24;
+
+	memset(&uvd->vcap, 0, sizeof(uvd->vcap));
+	strcpy(uvd->vcap.name, "IBM USB Camera");
+	uvd->vcap.type = VID_TYPE_CAPTURE;
+	uvd->vcap.channels = 1;
+	uvd->vcap.audios = 0;
+	uvd->vcap.maxwidth = VIDEOSIZE_X(uvd->canvas);
+	uvd->vcap.maxheight = VIDEOSIZE_Y(uvd->canvas);
+	uvd->vcap.minwidth = min_canvasWidth;
+	uvd->vcap.minheight = min_canvasHeight;
+
+	memset(&uvd->vchan, 0, sizeof(uvd->vchan));
+	uvd->vchan.flags = 0;
+	uvd->vchan.tuners = 0;
+	uvd->vchan.channel = 0;
+	uvd->vchan.type = VIDEO_TYPE_CAMERA;
+	strcpy(uvd->vchan.name, "Camera");
 }
 
 /*
- * usb_ibmcam_probe()
+ * ibmcam_probe()
  *
  * This procedure queries device descriptor and accepts the interface
  * if it looks like IBM C-it camera.
  *
  * History:
- * 1/22/00  Moved camera init code to ibmcam_open()
- * 1/27/00  Changed to use static structures, added locking.
- * 5/24/00  Corrected to prevent race condition (MOD_xxx_USE_COUNT).
- * 7/3/00   Fixed endianness bug.
- */
-static void *usb_ibmcam_probe(struct usb_device *dev, unsigned int ifnum,
-			 const struct usb_device_id *id)
-{
-	struct usb_ibmcam *ibmcam = NULL;
-	const struct usb_interface_descriptor *interface;
-	const struct usb_endpoint_descriptor *endpoint;
-	int devnum, model=0;
+ * 22-Jan-2000 Moved camera init code to ibmcam_open()
+ * 27=Jan-2000 Changed to use static structures, added locking.
+ * 24-May-2000 Corrected to prevent race condition (MOD_xxx_USE_COUNT).
+ * 03-Jul-2000 Fixed endianness bug.
+ * 12-Nov-2000 Reworked to comply with new probe() signature.
+ * 23-Jan-2001 Added compatibility with 2.2.x kernels.
+ */
+static void *ibmcam_probe(struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *devid)
+{
+	uvd_t *uvd = NULL;
+	int i, nas, model=0, canvasX=0, canvasY=0;
+	int actInterface=-1, inactInterface=-1, maxPS=0;
+	unsigned char video_ep = 0;
 
 	if (debug >= 1)
-		printk(KERN_DEBUG "ibmcam_probe(%p,%u.)\n", dev, ifnum);
+		info("ibmcam_probe(%p,%u.)", dev, ifnum);
 
 	/* We don't handle multi-config cameras */
 	if (dev->descriptor.bNumConfigurations != 1)
 		return NULL;
 
+	/* Is it an IBM camera? */
+	if (dev->descriptor.idVendor != IBMCAM_VENDOR_ID)
+		return NULL;
+	if ((dev->descriptor.idProduct != IBMCAM_PRODUCT_ID) &&
+	    (dev->descriptor.idProduct != NETCAM_PRODUCT_ID))
+		return NULL;
+
 	/* Check the version/revision */
 	switch (dev->descriptor.bcdDevice) {
 	case 0x0002:
 		if (ifnum != 2)
 			return NULL;
-		printk(KERN_INFO "IBM USB camera found (model 1, rev. 0x%04x).\n",
-			dev->descriptor.bcdDevice);
 		model = IBMCAM_MODEL_1;
 		break;
 	case 0x030A:
 		if (ifnum != 0)
 			return NULL;
-		printk(KERN_INFO "IBM USB camera found (model 2, rev. 0x%04x).\n",
-			dev->descriptor.bcdDevice);
-		model = IBMCAM_MODEL_2;
+		if (dev->descriptor.idProduct == NETCAM_PRODUCT_ID)
+			model = IBMCAM_MODEL_4;
+		else
+			model = IBMCAM_MODEL_2;
 		break;
-
-	/* ibmcam_table contents prevents any other values from ever
-	   being passed to us, so no need for "default" case. */
+	case 0x0301:
+		if (ifnum != 0)
+			return NULL;
+		model = IBMCAM_MODEL_3;
+		break;
+	default:
+		err("IBM camera with revision 0x%04x is not supported.",
+			dev->descriptor.bcdDevice);
+		return NULL;
 	}
+	info("IBM USB camera found (model %d, rev. 0x%04x)",
+		model, dev->descriptor.bcdDevice);
 
 	/* Validate found interface: must have one ISO endpoint */
-	interface = &dev->actconfig->interface[ifnum].altsetting[0];
-	if (interface->bNumEndpoints != 1) {
-		printk(KERN_ERR "IBM camera: interface %d. has %u. endpoints!\n",
-		       ifnum, (unsigned)(interface->bNumEndpoints));
+	nas = dev->actconfig->interface[ifnum].num_altsetting;
+	if (debug > 0)
+		info("Number of alternate settings=%d.", nas);
+	if (nas < 2) {
+		err("Too few alternate settings for this camera!");
 		return NULL;
 	}
-	endpoint = &interface->endpoint[0];
-	if ((endpoint->bmAttributes & 0x03) != 0x01) {
-		printk(KERN_ERR "IBM camera: interface %d. has non-ISO endpoint!\n", ifnum);
-		return NULL;
+	/* Validate all alternate settings */
+	for (i=0; i < nas; i++) {
+		const struct usb_interface_descriptor *interface;
+		const struct usb_endpoint_descriptor *endpoint;
+
+		interface = &dev->actconfig->interface[ifnum].altsetting[i];
+		if (interface->bNumEndpoints != 1) {
+			err("Interface %d. has %u. endpoints!",
+			    ifnum, (unsigned)(interface->bNumEndpoints));
+			return NULL;
+		}
+		endpoint = &interface->endpoint[0];
+		if (video_ep == 0)
+			video_ep = endpoint->bEndpointAddress;
+		else if (video_ep != endpoint->bEndpointAddress) {
+			err("Alternate settings have different endpoint addresses!");
+			return NULL;
+		}
+		if ((endpoint->bmAttributes & 0x03) != 0x01) {
+			err("Interface %d. has non-ISO endpoint!", ifnum);
+			return NULL;
+		}
+		if ((endpoint->bEndpointAddress & 0x80) == 0) {
+			err("Interface %d. has ISO OUT endpoint!", ifnum);
+			return NULL;
+		}
+		if (endpoint->wMaxPacketSize == 0) {
+			if (inactInterface < 0)
+				inactInterface = i;
+			else {
+				err("More than one inactive alt. setting!");
+				return NULL;
+			}
+		} else {
+			if (actInterface < 0) {
+				actInterface = i;
+				maxPS = endpoint->wMaxPacketSize;
+				if (debug > 0)
+					info("Active setting=%d. maxPS=%d.", i, maxPS);
+			} else
+				err("More than one active alt. setting! Ignoring #%d.", i);
+		}
 	}
-	if ((endpoint->bEndpointAddress & 0x80) == 0) {
-		printk(KERN_ERR "IBM camera: interface %d. has ISO OUT endpoint!\n", ifnum);
+	if ((maxPS <= 0) || (actInterface < 0) || (inactInterface < 0)) {
+		err("Failed to recognize the camera!");
 		return NULL;
 	}
 
 	/* Validate options */
-	if (model == IBMCAM_MODEL_1) {
+	switch (model) {
+	case IBMCAM_MODEL_1:
 		RESTRICT_TO_RANGE(lighting, 0, 2);
-		RESTRICT_TO_RANGE(videosize, VIDEOSIZE_128x96, VIDEOSIZE_352x288);
-	} else {
+		RESTRICT_TO_RANGE(size, SIZE_128x96, SIZE_352x288);
+		if (framerate < 0)
+			framerate = 2;
+		canvasX = 352;
+		canvasY = 288;
+		break;
+	case IBMCAM_MODEL_2:
 		RESTRICT_TO_RANGE(lighting, 0, 15);
-		RESTRICT_TO_RANGE(videosize, VIDEOSIZE_176x144, VIDEOSIZE_352x240);
+		RESTRICT_TO_RANGE(size, SIZE_176x144, SIZE_352x240);
+		if (framerate < 0)
+			framerate = 2;
+		canvasX = 352;
+		canvasY = 240;
+		break;
+	case IBMCAM_MODEL_3:
+		RESTRICT_TO_RANGE(lighting, 0, 15); /* FIXME */
+		switch (size) {
+		case SIZE_160x120:
+			canvasX = 160;
+			canvasY = 120;
+			if (framerate < 0)
+				framerate = 2;
+			RESTRICT_TO_RANGE(framerate, 0, 5);
+			break;
+		default:
+			info("IBM camera: using 320x240");
+			size = SIZE_320x240;
+			/* No break here */
+		case SIZE_320x240:
+			canvasX = 320;
+			canvasY = 240;
+			if (framerate < 0)
+				framerate = 3;
+			RESTRICT_TO_RANGE(framerate, 0, 5);
+			break;
+		case SIZE_640x480:
+			canvasX = 640;
+			canvasY = 480;
+			framerate = 0;	/* Slowest, and maybe even that is too fast */
+			break;
+		}
+		break;
+	case IBMCAM_MODEL_4:
+		RESTRICT_TO_RANGE(lighting, 0, 2);
+		switch (size) {
+		case SIZE_128x96:
+			canvasX = 128;
+			canvasY = 96;
+			break;
+		case SIZE_160x120:
+			canvasX = 160;
+			canvasY = 120;
+			break;
+		default:
+			info("IBM NetCamera: using 176x144");
+			size = SIZE_176x144;
+			/* No break here */
+		case SIZE_176x144:
+			canvasX = 176;
+			canvasY = 144;
+			break;
+		case SIZE_320x240:
+			canvasX = 320;
+			canvasY = 240;
+			break;
+		case SIZE_352x288:
+			canvasX = 352;
+			canvasY = 288;
+			break;
+		}
+		break;
+	default:
+		err("IBM camera: Model %d. not supported!", model);
+		return NULL;
 	}
 
 	/* Code below may sleep, need to lock module while we are here */
 	MOD_INC_USE_COUNT;
-
-	devnum = ibmcam_find_struct();
-	if (devnum == -1) {
-		printk(KERN_INFO "IBM USB camera driver: Too many devices!\n");
-		ibmcam = NULL; /* Do not free, it's preallocated */
-		goto probe_done;
-	}
-	ibmcam = &cams[devnum];
-
-	down(&ibmcam->lock);
-	ibmcam->camera_model = model;
-	ibmcam->remove_pending = 0;
-	ibmcam->last_error = 0;
-	ibmcam->dev = dev;
-	ibmcam->iface = ifnum;
-	ibmcam->ifaceAltInactive = 0;
-	ibmcam->ifaceAltActive = 1;
-	ibmcam->video_endp = endpoint->bEndpointAddress;
-	ibmcam->iso_packet_len = 1014;
-	ibmcam->compress = 0;
-	ibmcam->user=0; 
-
-	usb_ibmcam_configure_video(ibmcam);
-	up (&ibmcam->lock);
-
-	if (video_register_device(&ibmcam->vdev, VFL_TYPE_GRABBER, video_nr) == -1) {
-		printk(KERN_ERR "video_register_device failed\n");
-		ibmcam = NULL; /* Do not free, it's preallocated */
-	}
-	if (debug > 1)
-		printk(KERN_DEBUG "video_register_device() successful\n");
-probe_done:
-	MOD_DEC_USE_COUNT;
-	return ibmcam;
-}
-
-/*
- * usb_ibmcam_release()
- *
- * This code does final release of struct usb_ibmcam. This happens
- * after the device is disconnected -and- all clients closed their files.
- *
- * History:
- * 1/27/00  Created.
- */
-static void usb_ibmcam_release(struct usb_ibmcam *ibmcam)
-{
-	video_unregister_device(&ibmcam->vdev);
-	if (debug > 0)
-		printk(KERN_DEBUG "usb_ibmcam_release: Video unregistered.\n");
-	ibmcam->ibmcam_used = 0;
-	ibmcam->initialized = 0;
-}
-
-/*
- * usb_ibmcam_disconnect()
- *
- * This procedure stops all driver activity, deallocates interface-private
- * structure (pointed by 'ptr') and after that driver should be removable
- * with no ill consequences.
- *
- * This code handles surprise removal. The ibmcam->user is a counter which
- * increments on open() and decrements on close(). If we see here that
- * this counter is not 0 then we have a client who still has us opened.
- * We set ibmcam->remove_pending flag as early as possible, and after that
- * all access to the camera will gracefully fail. These failures should
- * prompt client to (eventually) close the video device, and then - in
- * ibmcam_close() - we decrement ibmcam->ibmcam_used and usage counter.
- *
- * History:
- * 1/22/00  Added polling of MOD_IN_USE to delay removal until all users gone.
- * 1/27/00  Reworked to allow pending disconnects; see ibmcam_close()
- * 5/24/00  Corrected to prevent race condition (MOD_xxx_USE_COUNT).
- */
-static void usb_ibmcam_disconnect(struct usb_device *dev, void *ptr)
-{
-	static const char proc[] = "usb_ibmcam_disconnect";
-	struct usb_ibmcam *ibmcam = (struct usb_ibmcam *) ptr;
-
-	MOD_INC_USE_COUNT;
-
-	if (debug > 0)
-		printk(KERN_DEBUG "%s(%p,%p.)\n", proc, dev, ptr);
-
-	down(&ibmcam->lock);
-	ibmcam->remove_pending = 1; /* Now all ISO data will be ignored */
-
-	/* At this time we ask to cancel outstanding URBs */
-	ibmcam_stop_isoc(ibmcam);
-
-	ibmcam->dev = NULL;    	    /* USB device is no more */
-
-	if (ibmcam->user)
-		printk(KERN_INFO "%s: In use, disconnect pending.\n", proc);
-	else
-		usb_ibmcam_release(ibmcam);
-	up(&ibmcam->lock);
-	printk(KERN_INFO "IBM USB camera disconnected.\n");
-
+	uvd = usbvideo_AllocateDevice(cams);
+	if (uvd != NULL) {
+		/* Here uvd is a fully allocated uvd_t object */
+		uvd->flags = flags;
+		uvd->debug = debug;
+		uvd->dev = dev;
+		uvd->iface = ifnum;
+		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(canvasX, canvasY);
+		uvd->videosize = ibmcam_size_to_videosize(size);
+
+		/* Initialize ibmcam-specific data */
+		assert(IBMCAM_T(uvd) != NULL);
+		IBMCAM_T(uvd)->camera_model = model;
+		IBMCAM_T(uvd)->initialized = 0;
+
+		ibmcam_configure_video(uvd);
+
+		i = usbvideo_RegisterVideoDevice(uvd);
+		if (i != 0) {
+			err("usbvideo_RegisterVideoDevice() failed.");
+			uvd = NULL;
+		}
+	}
 	MOD_DEC_USE_COUNT;
+	return uvd;
 }
 
-static struct usb_device_id ibmcam_table [] = {
-	{ USB_DEVICE_VER(0x0545, 0x8080, 0x0002, 0x0002) },
-	{ USB_DEVICE_VER(0x0545, 0x8080, 0x030a, 0x030a) },
-	{ }						/* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE (usb, ibmcam_table);
-
-static struct usb_driver ibmcam_driver = {
-	name:		"ibmcam",
-	probe:		usb_ibmcam_probe,
-	disconnect:	usb_ibmcam_disconnect,
-	id_table:	ibmcam_table,
-};
-
 /*
- * usb_ibmcam_init()
+ * ibmcam_init()
  *
  * This code is run to initialize the driver.
  *
  * History:
- * 1/27/00  Reworked to use statically allocated usb_ibmcam structures.
+ * 1/27/00  Reworked to use statically allocated ibmcam structures.
+ * 21/10/00 Completely redesigned to use usbvideo services.
  */
-static int __init usb_ibmcam_init(void)
-{
-	unsigned u;
-
-	/* Initialize struct */
-	for (u = 0; u < MAX_IBMCAM; u++) {
-		struct usb_ibmcam *ibmcam = &cams[u];
-		memset (ibmcam, 0, sizeof(struct usb_ibmcam));
-	}
-	info(DRIVER_VERSION ":" DRIVER_DESC);
-	return usb_register(&ibmcam_driver);
-}
-
-static void __exit usb_ibmcam_cleanup(void)
+static int __init ibmcam_init(void)
 {
-	usb_deregister(&ibmcam_driver);
-}
+	usbvideo_cb_t cbTbl;
+	memset(&cbTbl, 0, sizeof(cbTbl));
+	cbTbl.probe = ibmcam_probe;
+	cbTbl.setupOnOpen = ibmcam_setup_on_open;
+	cbTbl.videoStart = ibmcam_video_start;
+	cbTbl.videoStop = ibmcam_video_stop;
+	cbTbl.processData = ibmcam_ProcessIsocData;
+	cbTbl.postProcess = usbvideo_DeinterlaceFrame;
+	cbTbl.adjustPicture = ibmcam_adjust_picture;
+	cbTbl.getFPS = ibmcam_calculate_fps;
+	return usbvideo_register(
+		&cams,
+		MAX_IBMCAM,
+		sizeof(ibmcam_t),
+		"ibmcam",
+		&cbTbl,
+		THIS_MODULE);
+}
+
+static void __exit ibmcam_cleanup(void)
+{
+	usbvideo_Deregister(&cams);
+}
+
+#if defined(usb_device_id_ver)
+
+static __devinitdata struct usb_device_id id_table[] = {
+	{ USB_DEVICE_VER(IBMCAM_VENDOR_ID, IBMCAM_PRODUCT_ID, 0x0002, 0x0002) },	/* Model 1 */
+	{ USB_DEVICE_VER(IBMCAM_VENDOR_ID, IBMCAM_PRODUCT_ID, 0x030a, 0x030a) },	/* Model 2 */
+	{ USB_DEVICE_VER(IBMCAM_VENDOR_ID, IBMCAM_PRODUCT_ID, 0x0301, 0x0301) },	/* Model 3 */
+	{ USB_DEVICE_VER(IBMCAM_VENDOR_ID, NETCAM_PRODUCT_ID, 0x030a, 0x030a) },	/* Model 4 */
+	{ }  /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, id_table);
 
-module_init(usb_ibmcam_init);
-module_exit(usb_ibmcam_cleanup);
+#endif /* defined(usb_device_id_ver) */
 
-MODULE_AUTHOR( DRIVER_AUTHOR );
-MODULE_DESCRIPTION( DRIVER_DESC );
+module_init(ibmcam_init);
+module_exit(ibmcam_cleanup);
diff -urN linux-2.4.11-pre4/drivers/usb/microtek.c linux/drivers/usb/microtek.c
--- linux-2.4.11-pre4/drivers/usb/microtek.c	Sat Sep 15 22:21:43 2001
+++ linux/drivers/usb/microtek.c	Mon Dec  3 23:16:38 2001
@@ -116,6 +116,7 @@
  *	20010320 check return value of scsi_register()
  *	20010320 Version 0.4.3
  *	20010408 Identify version on module load.
+ *	20011003 Fix multiple requests
  */
 
 #include <linux/module.h>
@@ -500,7 +501,6 @@
 		      context
 		);
 
-	transfer->transfer_flags = USB_ASYNC_UNLINK;
 	transfer->status = 0;
 
 	res = usb_submit_urb( transfer );
@@ -520,7 +520,6 @@
 
 	if ( context->final_callback )
 		context->final_callback(context->srb);
-	up( &context->instance->lock );
 
 }
 
@@ -710,7 +709,6 @@
 		goto out;
 	}
 
-	down(&desc->lock);
 	
 	FILL_BULK_URB(&desc->urb,
 		      desc->usb_dev,
@@ -733,7 +731,6 @@
 
 		if(callback)
 			callback(srb);
-	        up(&desc->lock);
 
 	}
 
diff -urN linux-2.4.11-pre4/drivers/usb/uhci.c linux/drivers/usb/uhci.c
--- linux-2.4.11-pre4/drivers/usb/uhci.c	Mon Sep 10 08:06:32 2001
+++ linux/drivers/usb/uhci.c	Mon Dec  3 23:16:38 2001
@@ -61,7 +61,7 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION ""
+#define DRIVER_VERSION "v1.1"
 #define DRIVER_AUTHOR "Linus 'Frodo Rabbit' Torvalds, Johannes Erdfelt, Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber"
 #define DRIVER_DESC "USB Universal Host Controller Interface driver"
 
@@ -1258,21 +1258,38 @@
 			return -ENOMEM;
 
 		uhci_add_td_to_urb(urb, td);
-		uhci_fill_td(td, status, destination | ((pktsze - 1) << 21) |
+		uhci_fill_td(td, status, destination |
+			(((pktsze - 1) & UHCI_NULL_DATA_SIZE) << 21) |
 			(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
 			 usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE),
 			data);
 
 		data += pktsze;
-		len -= maxsze;
-
-		if (len <= 0)
-			td->status |= TD_CTRL_IOC;
+		len -= pktsze;
 
 		usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
 			usb_pipeout(urb->pipe));
 	} while (len > 0);
 
+	if (usb_pipeout(urb->pipe) && (urb->transfer_flags & USB_ZERO_PACKET) &&
+	   urb->transfer_buffer_length) {
+		td = uhci_alloc_td(uhci, urb->dev);
+		if (!td)
+			return -ENOMEM;
+
+		uhci_add_td_to_urb(urb, td);
+		uhci_fill_td(td, status, destination | UHCI_NULL_DATA_SIZE |
+			(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+			 usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE),
+			data);
+
+		usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+			usb_pipeout(urb->pipe));
+	}
+
+	/* Set the flag on the last packet */
+	td->status |= TD_CTRL_IOC;
+
 	qh = uhci_alloc_qh(uhci, urb->dev);
 	if (!qh)
 		return -ENOMEM;
@@ -2343,11 +2360,11 @@
 
 	if (status & ~(USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD)) {
 		if (status & USBSTS_HSE)
-			printk(KERN_ERR "uhci: host system error, PCI problems?\n");
+			err("%x: host system error, PCI problems?", io_addr);
 		if (status & USBSTS_HCPE)
-			printk(KERN_ERR "uhci: host controller process error. something bad happened\n");
+			err("%x: host controller process error. something bad happened", io_addr);
 		if ((status & USBSTS_HCH) && !uhci->is_suspended) {
-			printk(KERN_ERR "uhci: host controller halted. very bad\n");
+			err("%x: host controller halted. very bad", io_addr);
 			/* FIXME: Reset the controller, fix the offending TD */
 		}
 	}
@@ -2463,37 +2480,74 @@
 	outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
 }
 
-static int uhci_alloc_root_hub(struct uhci *uhci)
+#ifdef CONFIG_PROC_FS
+static int uhci_num = 0;
+#endif
+
+static void free_uhci(struct uhci *uhci)
 {
-	struct usb_device *dev;
+	kfree(uhci);
+}
 
-	dev = usb_alloc_dev(NULL, uhci->bus);
-	if (!dev)
-		return -1;
+/*
+ * De-allocate all resources..
+ */
+static void release_uhci(struct uhci *uhci)
+{
+	int i;
+#ifdef CONFIG_PROC_FS
+	char buf[8];
+#endif
 
-	uhci->bus->root_hub = dev;
-	uhci->rh.dev = dev;
+	if (uhci->irq >= 0) {
+		free_irq(uhci->irq, uhci);
+		uhci->irq = -1;
+	}
 
-	return 0;
-}
+	for (i = 0; i < UHCI_NUM_SKELQH; i++)
+		if (uhci->skelqh[i]) {
+			uhci_free_qh(uhci, uhci->skelqh[i]);
+			uhci->skelqh[i] = NULL;
+		}
 
-static int uhci_start_root_hub(struct uhci *uhci)
-{
-	usb_connect(uhci->rh.dev);
+	for (i = 0; i < UHCI_NUM_SKELTD; i++)
+		if (uhci->skeltd[i]) {
+			uhci_free_td(uhci, uhci->skeltd[i]);
+			uhci->skeltd[i] = NULL;
+		}
 
-	if (usb_new_device(uhci->rh.dev) != 0) {
-		usb_free_dev(uhci->rh.dev);
+	if (uhci->qh_pool) {
+		pci_pool_destroy(uhci->qh_pool);
+		uhci->qh_pool = NULL;
+	}
 
-		return -1;
+	if (uhci->td_pool) {
+		pci_pool_destroy(uhci->td_pool);
+		uhci->td_pool = NULL;
 	}
 
-	return 0;
-}
+	if (uhci->fl) {
+		pci_free_consistent(uhci->dev, sizeof(*uhci->fl), uhci->fl, uhci->fl->dma_handle);
+		uhci->fl = NULL;
+	}
+
+	if (uhci->bus) {
+		usb_free_bus(uhci->bus);
+		uhci->bus = NULL;
+	}
 
 #ifdef CONFIG_PROC_FS
-static int uhci_num = 0;
+	if (uhci->proc_entry) {
+		sprintf(buf, "hc%d", uhci->num);
+
+		remove_proc_entry(buf, uhci_proc_root);
+		uhci->proc_entry = NULL;
+	}
 #endif
 
+	free_uhci(uhci);
+}
+
 /*
  * Allocate a frame list, and then setup the skeleton
  *
@@ -2504,27 +2558,96 @@
  *  - any isochronous events handled before any
  *    of the queues. We don't do that here, because
  *    we'll create the actual TD entries on demand.
- *  - The first queue is the "interrupt queue".
- *  - The second queue is the "control queue", split into low and high speed
- *  - The third queue is "bulk data".
+ *  - The first queue is the interrupt queue.
+ *  - The second queue is the control queue, split into low and high speed
+ *  - The third queue is bulk queue.
+ *  - The fourth queue is the bandwidth reclamation queue, which loops back
+ *    to the high speed control queue.
  */
-static struct uhci *alloc_uhci(struct pci_dev *dev, unsigned int io_addr, unsigned int io_size)
+static int alloc_uhci(struct pci_dev *dev, int irq, unsigned int io_addr, unsigned int io_size)
 {
-	int i, port;
 	struct uhci *uhci;
+	int retval = -EBUSY;
+	char buf[8], *bufp = buf;
+	int i, port;
 	struct usb_bus *bus;
 	dma_addr_t dma_handle;
+#ifdef CONFIG_PROC_FS
+	struct proc_dir_entry *ent;
+#endif
 
-	uhci = kmalloc(sizeof(*uhci), GFP_KERNEL);
-	if (!uhci)
-		return NULL;
+	if (!request_region(io_addr, io_size, "usb-uhci")) {
+		err("couldn't allocate I/O range %x - %x", io_addr,
+			io_addr + io_size - 1);
+		goto err_request_region;
+	}
+
+	if (!dev->irq) {
+		err("found UHCI device with no IRQ assigned. check BIOS settings!");
+		retval = -EINVAL;
+		goto err_invalid_irq;
+	}
+
+	if (!pci_dma_supported(dev, 0xFFFFFFFF)) {
+		err("PCI subsystem doesn't support 32 bit addressing?");
+		retval = -ENODEV;
+		goto err_pci_dma_supported;
+	}
+
+	if (pci_enable_device(dev) < 0) {
+		err("couldn't enable PCI device");
+		goto err_enable_device;
+	}
+
+	pci_set_master(dev);
+
+#ifndef __sparc__
+	sprintf(buf, "%d", irq);
+#else
+	bufp = __irq_itoa(irq);
+#endif
+	printk(KERN_INFO __FILE__ ": USB UHCI at I/O 0x%x, IRQ %s\n",
+		io_addr, bufp);
+
+	if (pci_set_dma_mask(dev, 0xFFFFFFFF)) {
+		err("couldn't set PCI dma mask");
+		retval = -ENODEV;
+		goto err_pci_set_dma_mask;
+	}
 
-	memset(uhci, 0, sizeof(*uhci));
+	uhci = kmalloc(sizeof(*uhci), GFP_KERNEL);
+	if (!uhci) {
+		err("couldn't allocate uhci structure");
+		retval = -ENOMEM;
+		goto err_alloc_uhci;
+	}
 
 	uhci->dev = dev;
-	uhci->irq = -1;
 	uhci->io_addr = io_addr;
 	uhci->io_size = io_size;
+	dev->driver_data = uhci;
+
+#ifdef CONFIG_PROC_FS
+	uhci->num = uhci_num++;
+
+	sprintf(buf, "hc%d", uhci->num);
+
+	ent = create_proc_entry(buf, S_IFREG|S_IRUGO|S_IWUSR, uhci_proc_root);
+	if (!ent) {
+		err("couldn't create uhci proc entry");
+		retval = -ENOMEM;
+		goto err_create_proc_entry;
+	}
+
+	ent->data = uhci;
+	ent->proc_fops = &uhci_proc_operations;
+	ent->size = 0;
+	uhci->proc_entry = ent;
+#endif
+
+	/* Reset here so we don't get any interrupts from an old setup */
+	/*  or broken setup */
+	reset_hc(uhci);
 
 	spin_lock_init(&uhci->qh_remove_list_lock);
 	INIT_LIST_HEAD(&uhci->qh_remove_list);
@@ -2542,10 +2665,13 @@
 
 	/* We need exactly one page (per UHCI specs), how convenient */
 	/* We assume that one page is atleast 4k (1024 frames * 4 bytes) */
+#if PAGE_SIZE < (4 * 1024)
+#error PAGE_SIZE is not atleast 4k
+#endif
 	uhci->fl = pci_alloc_consistent(uhci->dev, sizeof(*uhci->fl), &dma_handle);
 	if (!uhci->fl) {
-		printk(KERN_ERR "Unable to allocate consistent memory for frame list\n");
-		goto free_uhci;
+		err("unable to allocate consistent memory for frame list");
+		goto err_alloc_fl;
 	}
 
 	memset((void *)uhci->fl, 0, sizeof(*uhci->fl));
@@ -2555,34 +2681,38 @@
 	uhci->td_pool = pci_pool_create("uhci_td", uhci->dev,
 		sizeof(struct uhci_td), 16, 0, GFP_DMA | GFP_ATOMIC);
 	if (!uhci->td_pool) {
-		printk(KERN_ERR "Unable to create td pci_pool\n");
-		goto free_fl;
+		err("unable to create td pci_pool");
+		goto err_create_td_pool;
 	}
 
 	uhci->qh_pool = pci_pool_create("uhci_qh", uhci->dev,
 		sizeof(struct uhci_qh), 16, 0, GFP_DMA | GFP_ATOMIC);
 	if (!uhci->qh_pool) {
-		printk(KERN_ERR "Unable to create qh pci_pool\n");
-		goto free_td_pool;
+		err("unable to create qh pci_pool");
+		goto err_create_qh_pool;
 	}
 
 	bus = usb_alloc_bus(&uhci_device_operations);
-	if (!bus)
-		goto free_qh_pool;
+	if (!bus) {
+		err("unable to allocate bus");
+		goto err_alloc_bus;
+	}
 
 	uhci->bus = bus;
 	bus->hcpriv = uhci;
 
+	usb_register_bus(uhci->bus);
+
 	/* Initialize the root hub */
 
 	/* UHCI specs says devices must have 2 ports, but goes on to say */
 	/*  they may have more but give no way to determine how many they */
 	/*  have. However, according to the UHCI spec, Bit 7 is always set */
 	/*  to 1. So we try to use this to our advantage */
-	for (port = 0; port < (io_size - 0x10) / 2; port++) {
+	for (port = 0; port < (uhci->io_size - 0x10) / 2; port++) {
 		unsigned int portstatus;
 
-		portstatus = inw(io_addr + 0x10 + (port * 2));
+		portstatus = inw(uhci->io_addr + 0x10 + (port * 2));
 		if (!(portstatus & 0x0080))
 			break;
 	}
@@ -2598,15 +2728,16 @@
 
 	uhci->rh.numports = port;
 
-	if (uhci_alloc_root_hub(uhci)) {
+	uhci->bus->root_hub = uhci->rh.dev = usb_alloc_dev(NULL, uhci->bus);
+	if (!uhci->rh.dev) {
 		err("unable to allocate root hub");
-		goto free_fl;
+		goto err_alloc_root_hub;
 	}
 
 	uhci->skeltd[0] = uhci_alloc_td(uhci, uhci->rh.dev);
 	if (!uhci->skeltd[0]) {
 		err("unable to allocate TD 0");
-		goto free_fl;
+		goto err_alloc_skeltd;
 	}
 
 	/*
@@ -2619,7 +2750,7 @@
 		td = uhci->skeltd[i] = uhci_alloc_td(uhci, uhci->rh.dev);
 		if (!td) {
 			err("unable to allocate TD %d", i);
-			goto free_tds;
+			goto err_alloc_skeltd;
 		}
 
 		uhci_fill_td(td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN, 0);
@@ -2628,15 +2759,15 @@
 
 	uhci->skel_term_td = uhci_alloc_td(uhci, uhci->rh.dev);
 	if (!uhci->skel_term_td) {
-		err("unable to allocate TD 0");
-		goto free_fl;
+		err("unable to allocate skel TD term");
+		goto err_alloc_skeltd;
 	}
 
 	for (i = 0; i < UHCI_NUM_SKELQH; i++) {
 		uhci->skelqh[i] = uhci_alloc_qh(uhci, uhci->rh.dev);
 		if (!uhci->skelqh[i]) {
 			err("unable to allocate QH %d", i);
-			goto free_qhs;
+			goto err_alloc_skelqh;
 		}
 	}
 
@@ -2695,157 +2826,88 @@
 		uhci->fl->frame[i] =  uhci->skeltd[irq]->dma_handle;
 	}
 
-	return uhci;
-
-/*
- * error exits:
- */
-free_qhs:
-	for (i = 0; i < UHCI_NUM_SKELQH; i++)
-		if (uhci->skelqh[i]) {
-			uhci_free_qh(uhci, uhci->skelqh[i]);
-			uhci->skelqh[i] = NULL;
-		}
+	start_hc(uhci);
 
-free_tds:
-	for (i = 0; i < UHCI_NUM_SKELTD; i++)
-		if (uhci->skeltd[i]) {
-			uhci_free_td(uhci, uhci->skeltd[i]);
-			uhci->skeltd[i] = NULL;
-		}
+	if (request_irq(irq, uhci_interrupt, SA_SHIRQ, "usb-uhci", uhci))
+		goto err_request_irq;
 
-free_qh_pool:
-	pci_pool_destroy(uhci->qh_pool);
+	uhci->irq = irq;
 
-free_td_pool:
-	pci_pool_destroy(uhci->td_pool);
+	/* disable legacy emulation */
+	pci_write_config_word(uhci->dev, USBLEGSUP, 0);
 
-free_fl:
-	pci_free_consistent(uhci->dev, sizeof(*uhci->fl), uhci->fl, uhci->fl->dma_handle);
+	usb_connect(uhci->rh.dev);
 
-free_uhci:
-	kfree(uhci);
+	if (usb_new_device(uhci->rh.dev) != 0) {
+		err("unable to start root hub");
+		retval = -ENOMEM;
+		goto err_start_root_hub;
+	}
 
-	return NULL;
-}
+	return 0;
 
 /*
- * De-allocate all resources..
+ * error exits:
  */
-static void release_uhci(struct uhci *uhci)
-{
-	int i;
-#ifdef CONFIG_PROC_FS
-	char buf[8];
-#endif
-
-	if (uhci->irq >= 0) {
-		free_irq(uhci->irq, uhci);
-		uhci->irq = -1;
-	}
+err_start_root_hub:
+	free_irq(uhci->irq, uhci);
+	uhci->irq = -1;
 
+err_request_irq:
 	for (i = 0; i < UHCI_NUM_SKELQH; i++)
 		if (uhci->skelqh[i]) {
 			uhci_free_qh(uhci, uhci->skelqh[i]);
 			uhci->skelqh[i] = NULL;
 		}
 
+err_alloc_skelqh:
 	for (i = 0; i < UHCI_NUM_SKELTD; i++)
 		if (uhci->skeltd[i]) {
 			uhci_free_td(uhci, uhci->skeltd[i]);
 			uhci->skeltd[i] = NULL;
 		}
 
-	if (uhci->qh_pool) {
-		pci_pool_destroy(uhci->qh_pool);
-		uhci->qh_pool = NULL;
-	}
-
-	if (uhci->td_pool) {
-		pci_pool_destroy(uhci->td_pool);
-		uhci->td_pool = NULL;
-	}
-
-	if (uhci->fl) {
-		pci_free_consistent(uhci->dev, sizeof(*uhci->fl), uhci->fl, uhci->fl->dma_handle);
-		uhci->fl = NULL;
-	}
+err_alloc_skeltd:
+	usb_free_dev(uhci->rh.dev);
+	uhci->rh.dev = NULL;
 
+err_alloc_root_hub:
 	usb_free_bus(uhci->bus);
+	uhci->bus = NULL;
 
-#ifdef CONFIG_PROC_FS
-	sprintf(buf, "hc%d", uhci->num);
-
-	remove_proc_entry(buf, uhci_proc_root);
-	uhci->proc_entry = NULL;
-#endif
-
-	kfree(uhci);
-}
-
-/*
- * If we've successfully found a UHCI, now is the time to return success..
- */
-static int setup_uhci(struct pci_dev *dev, int irq, unsigned int io_addr, unsigned int io_size)
-{
-	int retval;
-	struct uhci *uhci;
-	char buf[8], *bufp = buf;
-#ifdef CONFIG_PROC_FS
-	struct proc_dir_entry *ent;
-#endif
-
-#ifndef __sparc__
-	sprintf(buf, "%d", irq);
-#else
-	bufp = __irq_itoa(irq);
-#endif
-	printk(KERN_INFO __FILE__ ": USB UHCI at I/O 0x%x, IRQ %s\n",
-		io_addr, bufp);
+err_alloc_bus:
+	pci_pool_destroy(uhci->qh_pool);
+	uhci->qh_pool = NULL;
 
-	uhci = alloc_uhci(dev, io_addr, io_size);
-	if (!uhci)
-		return -ENOMEM;
+err_create_qh_pool:
+	pci_pool_destroy(uhci->td_pool);
+	uhci->td_pool = NULL;
 
-	dev->driver_data = uhci;
+err_create_td_pool:
+	pci_free_consistent(uhci->dev, sizeof(*uhci->fl), uhci->fl, uhci->fl->dma_handle);
+	uhci->fl = NULL;
 
+err_alloc_fl:
 #ifdef CONFIG_PROC_FS
-	uhci->num = uhci_num++;
-
-	sprintf(buf, "hc%d", uhci->num);
-
-	ent = create_proc_entry(buf, S_IFREG|S_IRUGO|S_IWUSR, uhci_proc_root);
-	if (!ent)
-		return -ENOMEM;
+	remove_proc_entry(buf, uhci_proc_root);
+	uhci->proc_entry = NULL;
 
-	ent->data = uhci;
-	ent->proc_fops = &uhci_proc_operations;
-	ent->size = 0;
-	uhci->proc_entry = ent;
+err_create_proc_entry:
+	free_uhci(uhci);
 #endif
 
-	request_region(uhci->io_addr, io_size, "usb-uhci");
-
-	reset_hc(uhci);
-
-	usb_register_bus(uhci->bus);
-	start_hc(uhci);
+err_alloc_uhci:
 
-	retval = -EBUSY;
-	if (request_irq(irq, uhci_interrupt, SA_SHIRQ, "usb-uhci", uhci) == 0) {
-		uhci->irq = irq;
+err_pci_set_dma_mask:
 
-		pci_write_config_word(dev, USBLEGSUP, USBLEGSUP_DEFAULT);
+err_enable_device:
 
-		if (!uhci_start_root_hub(uhci))
-			return 0;
-	}
+err_pci_dma_supported:
+	release_region(io_addr, io_size);
 
-	/* Couldn't allocate IRQ if we got here */
+err_invalid_irq:
 
-	reset_hc(uhci);
-	release_region(uhci->io_addr, uhci->io_size);
-	release_uhci(uhci);
+err_request_region:
 
 	return retval;
 }
@@ -2854,23 +2916,6 @@
 {
 	int i;
 
-	if (!pci_dma_supported(dev, 0xFFFFFFFF)) {
-		err("PCI subsystem doesn't support 32 bit addressing?");
-		return -ENODEV;
-	}
-	dev->dma_mask = 0xFFFFFFFF;
-
-	/* disable legacy emulation */
-	pci_write_config_word(dev, USBLEGSUP, 0);
-
-	if (pci_enable_device(dev) < 0)
-		return -ENODEV;
-
-	if (!dev->irq) {
-		err("found UHCI device with no IRQ assigned. check BIOS settings!");
-		return -ENODEV;
-	}
-
 	/* Search for the IO base address.. */
 	for (i = 0; i < 6; i++) {
 		unsigned int io_addr = pci_resource_start(dev, i);
@@ -2880,12 +2925,7 @@
 		if (!(pci_resource_flags(dev, i) & IORESOURCE_IO))
 			continue;
 
-		/* Is it already in use? */
-		if (check_region(io_addr, io_size))
-			break;
-
-		pci_set_master(dev);
-		return setup_uhci(dev, dev->irq, io_addr, io_size);
+		return alloc_uhci(dev, dev->irq, io_addr, io_size);
 	}
 
 	return -ENODEV;
@@ -2945,11 +2985,11 @@
 	}, { /* end: all zeroes */ }
 };
 
-MODULE_DEVICE_TABLE (pci, uhci_pci_ids);
+MODULE_DEVICE_TABLE(pci, uhci_pci_ids);
 
 static struct pci_driver uhci_pci_driver = {
 	name:		"usb-uhci",
-	id_table:	&uhci_pci_ids [0],
+	id_table:	uhci_pci_ids,
 
 	probe:		uhci_pci_probe,
 	remove:		uhci_pci_remove,
@@ -2965,6 +3005,8 @@
 {
 	int retval = -ENOMEM;
 
+	info(DRIVER_DESC " " DRIVER_VERSION);
+
 	if (debug) {
 		errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL);
 		if (!errbuf)
@@ -2986,8 +3028,6 @@
 	if (retval)
 		goto init_failed;
 
-	info(DRIVER_VERSION ":" DRIVER_DESC);
-
 	return 0;
 
 init_failed:
@@ -3009,7 +3049,7 @@
 	return retval;
 }
 
-static void __exit uhci_hcd_cleanup (void) 
+static void __exit uhci_hcd_cleanup(void) 
 {
 	pci_unregister_driver(&uhci_pci_driver);
 	
@@ -3027,6 +3067,6 @@
 module_init(uhci_hcd_init);
 module_exit(uhci_hcd_cleanup);
 
-MODULE_AUTHOR( DRIVER_AUTHOR );
-MODULE_DESCRIPTION( DRIVER_DESC );
-
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff -urN linux-2.4.11-pre4/drivers/usb/ultracam.c linux/drivers/usb/ultracam.c
--- linux-2.4.11-pre4/drivers/usb/ultracam.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/usb/ultracam.c	Mon Dec  3 23:16:38 2001
@@ -0,0 +1,710 @@
+/*
+ * USB NB Camera driver
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/wrapper.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include "usbvideo.h"
+
+#define	ULTRACAM_VENDOR_ID	0x0461
+#define	ULTRACAM_PRODUCT_ID	0x0813
+
+#define MAX_CAMERAS		4	/* How many devices we allow to connect */
+
+/*
+ * This structure lives in uvd_t->user field.
+ */
+typedef struct {
+	int initialized;	/* Had we already sent init sequence? */
+	int camera_model;	/* What type of IBM camera we got? */
+        int has_hdr;
+} ultracam_t;
+#define	ULTRACAM_T(uvd)	((ultracam_t *)((uvd)->user_data))
+
+static usbvideo_t *cams = NULL;
+
+static int debug = 0;
+
+static int flags = 0; /* FLAGS_DISPLAY_HINTS | FLAGS_OVERLAY_STATS; */
+
+static const int min_canvasWidth  = 8;
+static const int min_canvasHeight = 4;
+
+//static int lighting = 1; /* Medium */
+
+#define SHARPNESS_MIN	0
+#define SHARPNESS_MAX	6
+//static int sharpness = 4; /* Low noise, good details */
+
+#define FRAMERATE_MIN	0
+#define FRAMERATE_MAX	6
+static int framerate = -1;
+
+/*
+ * Here we define several initialization variables. They may
+ * be used to automatically set color, hue, brightness and
+ * contrast to desired values. This is particularly useful in
+ * case of webcams (which have no controls and no on-screen
+ * output) and also when a client V4L software is used that
+ * does not have some of those controls. In any case it's
+ * good to have startup values as options.
+ *
+ * These values are all in [0..255] range. This simplifies
+ * operation. Note that actual values of V4L variables may
+ * be scaled up (as much as << 8). User can see that only
+ * on overlay output, however, or through a V4L client.
+ */
+static int init_brightness = 128;
+static int init_contrast = 192;
+static int init_color = 128;
+static int init_hue = 128;
+static int hue_correction = 128;
+
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debug level: 0-9 (default=0)");
+MODULE_PARM(flags, "i");
+MODULE_PARM_DESC(flags,
+		"Bitfield: 0=VIDIOCSYNC, "
+		"1=B/W, "
+		"2=show hints, "
+		"3=show stats, "
+		"4=test pattern, "
+		"5=separate frames, "
+		"6=clean frames");
+MODULE_PARM(framerate, "i");
+MODULE_PARM_DESC(framerate, "Framerate setting: 0=slowest, 6=fastest (default=2)");
+MODULE_PARM(lighting, "i");
+MODULE_PARM_DESC(lighting, "Photosensitivity: 0=bright, 1=medium (default), 2=low light");
+MODULE_PARM(sharpness, "i");
+MODULE_PARM_DESC(sharpness, "Model1 noise reduction: 0=smooth, 6=sharp (default=4)");
+
+MODULE_PARM(init_brightness, "i");
+MODULE_PARM_DESC(init_brightness, "Brightness preconfiguration: 0-255 (default=128)");
+MODULE_PARM(init_contrast, "i");
+MODULE_PARM_DESC(init_contrast, "Contrast preconfiguration: 0-255 (default=192)");
+MODULE_PARM(init_color, "i");
+MODULE_PARM_DESC(init_color, "Color preconfiguration: 0-255 (default=128)");
+MODULE_PARM(init_hue, "i");
+MODULE_PARM_DESC(init_hue, "Hue preconfiguration: 0-255 (default=128)");
+MODULE_PARM(hue_correction, "i");
+MODULE_PARM_DESC(hue_correction, "YUV colorspace regulation: 0-255 (default=128)");
+
+/*
+ * ultracam_ProcessIsocData()
+ *
+ * Generic routine to parse the ring queue data. It employs either
+ * ultracam_find_header() or ultracam_parse_lines() to do most
+ * of work.
+ *
+ * 02-Nov-2000 First (mostly dummy) version.
+ * 06-Nov-2000 Rewrote to dump all data into frame.
+ */
+void ultracam_ProcessIsocData(uvd_t *uvd, usbvideo_frame_t *frame)
+{
+	int n;
+
+	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++;
+	}
+}
+
+/*
+ * ultracam_veio()
+ *
+ * History:
+ * 1/27/00  Added check for dev == NULL; this happens if camera is unplugged.
+ */
+static int ultracam_veio(
+	uvd_t *uvd,
+	unsigned char req,
+	unsigned short value,
+	unsigned short index,
+	int is_out)
+{
+	static const char proc[] = "ultracam_veio";
+	unsigned char cp[8] /* = { 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef } */;
+	int i;
+
+	if (!CAMERA_IS_OPERATIONAL(uvd))
+		return 0;
+
+	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),
+			HZ);
+#if 1
+		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,
+			usb_sndctrlpipe(uvd->dev, 0),
+			req,
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			value,
+			index,
+			NULL,
+			0,
+			HZ);
+	}
+	if (i < 0) {
+		err("%s: ERROR=%d. Camera stopped; Reconnect or reload driver.",
+		    proc, i);
+		uvd->last_error = i;
+	}
+	return i;
+}
+
+/*
+ * ultracam_calculate_fps()
+ */
+static int ultracam_calculate_fps(uvd_t *uvd)
+{
+	return 3 + framerate*4 + framerate/2;
+}
+
+/*
+ * ultracam_adjust_contrast()
+ */
+static void ultracam_adjust_contrast(uvd_t *uvd)
+{
+}
+
+/*
+ * ultracam_change_lighting_conditions()
+ */
+static void ultracam_change_lighting_conditions(uvd_t *uvd)
+{
+}
+
+/*
+ * ultracam_set_sharpness()
+ *
+ * Cameras model 1 have internal smoothing feature. It is controlled by value in
+ * range [0..6], where 0 is most smooth and 6 is most sharp (raw image, I guess).
+ * Recommended value is 4. Cameras model 2 do not have this feature at all.
+ */
+static void ultracam_set_sharpness(uvd_t *uvd)
+{
+}
+
+/*
+ * ultracam_set_brightness()
+ *
+ * This procedure changes brightness of the picture.
+ */
+static void ultracam_set_brightness(uvd_t *uvd)
+{
+}
+
+static void ultracam_set_hue(uvd_t *uvd)
+{
+}
+
+/*
+ * ultracam_adjust_picture()
+ *
+ * This procedure gets called from V4L interface to update picture settings.
+ * Here we change brightness and contrast.
+ */
+static void ultracam_adjust_picture(uvd_t *uvd)
+{
+	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(uvd_t *uvd)
+{
+}
+
+/*
+ * ultracam_reinit_iso()
+ *
+ * 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(uvd_t *uvd, int do_stop)
+{
+}
+
+static void ultracam_video_start(uvd_t *uvd)
+{
+	ultracam_change_lighting_conditions(uvd);
+	ultracam_set_sharpness(uvd);
+	ultracam_reinit_iso(uvd, 0);
+}
+
+static int ultracam_resetPipe(uvd_t *uvd)
+{
+	usb_clear_halt(uvd->dev, uvd->video_endp);
+	return 0;
+}
+
+static int ultracam_alternateSetting(uvd_t *uvd, int setting)
+{
+	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;
+	}
+	return 0;
+}
+
+/*
+ * Return negative code on failure, 0 on success.
+ */
+static int ultracam_setup_on_open(uvd_t *uvd)
+{
+	int setup_ok = 0; /* Success by default */
+	/* 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);
+		ultracam_resetPipe(uvd);
+		ULTRACAM_T(uvd)->initialized = (setup_ok != 0);
+	}
+	return setup_ok;
+}
+
+static void ultracam_configure_video(uvd_t *uvd)
+{
+	if (uvd == NULL)
+		return;
+
+	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);
+
+	memset(&uvd->vpic, 0, sizeof(uvd->vpic));
+	memset(&uvd->vpic_old, 0x55, sizeof(uvd->vpic_old));
+
+	uvd->vpic.colour = init_color << 8;
+	uvd->vpic.hue = init_hue << 8;
+	uvd->vpic.brightness = init_brightness << 8;
+	uvd->vpic.contrast = init_contrast << 8;
+	uvd->vpic.whiteness = 105 << 8; /* This one isn't used */
+	uvd->vpic.depth = 24;
+	uvd->vpic.palette = VIDEO_PALETTE_RGB24;
+
+	memset(&uvd->vcap, 0, sizeof(uvd->vcap));
+	strcpy(uvd->vcap.name, "IBM Ultra Camera");
+	uvd->vcap.type = VID_TYPE_CAPTURE;
+	uvd->vcap.channels = 1;
+	uvd->vcap.audios = 0;
+	uvd->vcap.maxwidth = VIDEOSIZE_X(uvd->canvas);
+	uvd->vcap.maxheight = VIDEOSIZE_Y(uvd->canvas);
+	uvd->vcap.minwidth = min_canvasWidth;
+	uvd->vcap.minheight = min_canvasHeight;
+
+	memset(&uvd->vchan, 0, sizeof(uvd->vchan));
+	uvd->vchan.flags = 0;
+	uvd->vchan.tuners = 0;
+	uvd->vchan.channel = 0;
+	uvd->vchan.type = VIDEO_TYPE_CAMERA;
+	strcpy(uvd->vchan.name, "Camera");
+}
+
+/*
+ * 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 void *ultracam_probe(struct usb_device *dev, unsigned int ifnum
+#if defined(usb_device_id_ver)
+	,const struct usb_device_id *devid
+#endif
+	)
+{
+	uvd_t *uvd = NULL;
+	int i, nas;
+	int actInterface=-1, inactInterface=-1, maxPS=0;
+	unsigned char video_ep = 0;
+
+	if (debug >= 1)
+		info("ultracam_probe(%p,%u.)", dev, ifnum);
+
+	/* We don't handle multi-config cameras */
+	if (dev->descriptor.bNumConfigurations != 1)
+		return NULL;
+
+	/* Is it an IBM camera? */
+	if ((dev->descriptor.idVendor != ULTRACAM_VENDOR_ID) ||
+	    (dev->descriptor.idProduct != ULTRACAM_PRODUCT_ID))
+		return NULL;
+
+	info("IBM Ultra camera found (rev. 0x%04x)", dev->descriptor.bcdDevice);
+
+	/* Validate found interface: must have one ISO endpoint */
+	nas = dev->actconfig->interface[ifnum].num_altsetting;
+	if (debug > 0)
+		info("Number of alternate settings=%d.", nas);
+	if (nas < 8) {
+		err("Too few alternate settings for this camera!");
+		return NULL;
+	}
+	/* Validate all alternate settings */
+	for (i=0; i < nas; i++) {
+		const struct usb_interface_descriptor *interface;
+		const struct usb_endpoint_descriptor *endpoint;
+
+		interface = &dev->actconfig->interface[ifnum].altsetting[i];
+		if (interface->bNumEndpoints != 1) {
+			err("Interface %d. has %u. endpoints!",
+			    ifnum, (unsigned)(interface->bNumEndpoints));
+			return NULL;
+		}
+		endpoint = &interface->endpoint[0];
+		if (video_ep == 0)
+			video_ep = endpoint->bEndpointAddress;
+		else if (video_ep != endpoint->bEndpointAddress) {
+			err("Alternate settings have different endpoint addresses!");
+			return NULL;
+		}
+		if ((endpoint->bmAttributes & 0x03) != 0x01) {
+			err("Interface %d. has non-ISO endpoint!", ifnum);
+			return NULL;
+		}
+		if ((endpoint->bEndpointAddress & 0x80) == 0) {
+			err("Interface %d. has ISO OUT endpoint!", ifnum);
+			return NULL;
+		}
+		if (endpoint->wMaxPacketSize == 0) {
+			if (inactInterface < 0)
+				inactInterface = i;
+			else {
+				err("More than one inactive alt. setting!");
+				return NULL;
+			}
+		} else {
+			if (actInterface < 0) {
+				actInterface = i;
+				maxPS = endpoint->wMaxPacketSize;
+				if (debug > 0)
+					info("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 ctive setting=%d. maxPS=%d.",
+						     i, maxPS);
+					}
+				}
+			}
+		}
+	}
+	if ((maxPS <= 0) || (actInterface < 0) || (inactInterface < 0)) {
+		err("Failed to recognize the camera!");
+		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) {
+		/* Here uvd is a fully allocated uvd_t object */
+		uvd->flags = flags;
+		uvd->debug = debug;
+		uvd->dev = dev;
+		uvd->iface = ifnum;
+		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;
+		}
+	}
+	MOD_DEC_USE_COUNT;
+	return uvd;
+}
+
+/*
+ * ultracam_init()
+ *
+ * This code is run to initialize the driver.
+ */
+static int __init ultracam_init(void)
+{
+	usbvideo_cb_t cbTbl;
+	memset(&cbTbl, 0, sizeof(cbTbl));
+	cbTbl.probe = ultracam_probe;
+	cbTbl.setupOnOpen = ultracam_setup_on_open;
+	cbTbl.videoStart = ultracam_video_start;
+	cbTbl.videoStop = ultracam_video_stop;
+	cbTbl.processData = ultracam_ProcessIsocData;
+	cbTbl.postProcess = usbvideo_DeinterlaceFrame;
+	cbTbl.adjustPicture = ultracam_adjust_picture;
+	cbTbl.getFPS = ultracam_calculate_fps;
+	return usbvideo_register(
+		&cams,
+		MAX_CAMERAS,
+		sizeof(ultracam_t),
+		"ultracam",
+		&cbTbl,
+		THIS_MODULE);
+}
+
+static void __exit ultracam_cleanup(void)
+{
+	usbvideo_Deregister(&cams);
+}
+
+#if defined(usb_device_id_ver)
+
+static __devinitdata struct usb_device_id id_table[] = {
+	{ USB_DEVICE(ULTRACAM_VENDOR_ID, ULTRACAM_PRODUCT_ID) },
+	{ }  /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+
+#endif /* defined(usb_device_id_ver) */
+MODULE_LICENSE("GPL");
+
+module_init(ultracam_init);
+module_exit(ultracam_cleanup);
diff -urN linux-2.4.11-pre4/drivers/usb/usb-uhci.c linux/drivers/usb/usb-uhci.c
--- linux-2.4.11-pre4/drivers/usb/usb-uhci.c	Mon Dec  3 23:16:28 2001
+++ linux/drivers/usb/usb-uhci.c	Mon Dec  3 23:16:38 2001
@@ -71,9 +71,6 @@
 #define DEBUG_SYMBOLS
 #ifdef DEBUG_SYMBOLS
 	#define _static
-	#ifndef EXPORT_SYMTAB
-		#define EXPORT_SYMTAB
-	#endif
 #else
 	#define _static static
 #endif
diff -urN linux-2.4.11-pre4/drivers/usb/usbvideo.h linux/drivers/usb/usbvideo.h
--- linux-2.4.11-pre4/drivers/usb/usbvideo.h	Fri Sep  7 09:28:37 2001
+++ linux/drivers/usb/usbvideo.h	Mon Dec  3 23:16:38 2001
@@ -255,13 +255,7 @@
  * that default to usbvideo-provided methods.
  */
 typedef struct {
-#if defined(usb_device_id_ver)
-	/* New style probe (for 2.4.x kernels with hotplugging) */
 	void *(*probe)(struct usb_device *, unsigned int,const struct usb_device_id *);
-#else
-	/* Old style probe (for 2.2.x kernels) */
-	void *(*probe)(struct usb_device *, unsigned int);
-#endif
 	void (*userFree)(uvd_t *);
 	void (*disconnect)(struct usb_device *, void *);
 	int (*setupOnOpen)(uvd_t *);
diff -urN linux-2.4.11-pre4/fs/attr.c linux/fs/attr.c
--- linux-2.4.11-pre4/fs/attr.c	Mon Sep 10 07:31:25 2001
+++ linux/fs/attr.c	Mon Dec  3 23:16:38 2001
@@ -11,6 +11,7 @@
 #include <linux/smp_lock.h>
 #include <linux/dnotify.h>
 #include <linux/fcntl.h>
+#include <linux/quotaops.h>
 
 /* Taken over from the old code... */
 
@@ -131,8 +132,13 @@
 		error = inode->i_op->setattr(dentry, attr);
 	else {
 		error = inode_change_ok(inode, attr);
-		if (!error)
-			inode_setattr(inode, attr);
+		if (!error) {
+			if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) ||
+			    (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid))
+				error = DQUOT_TRANSFER(inode, attr) ? -EDQUOT : 0;
+			if (!error)
+				inode_setattr(inode, attr);
+		}
 	}
 	unlock_kernel();
 	if (!error) {
diff -urN linux-2.4.11-pre4/fs/binfmt_elf.c linux/fs/binfmt_elf.c
--- linux-2.4.11-pre4/fs/binfmt_elf.c	Mon Dec  3 23:16:28 2001
+++ linux/fs/binfmt_elf.c	Mon Dec  3 23:16:38 2001
@@ -32,6 +32,7 @@
 #include <linux/highuid.h>
 #include <linux/smp_lock.h>
 #include <linux/compiler.h>
+#include <linux/limits.h>
 
 #include <asm/uaccess.h>
 #include <asm/param.h>
@@ -444,9 +445,11 @@
 	/* Now read in all of the header information */
 
 	retval = -ENOMEM;
-	size = ((unsigned int)elf_ex.e_phentsize) * elf_ex.e_phnum;
-	if (size > 65536)
+	if (elf_ex.e_phentsize != sizeof(struct elf_phdr))
 		goto out;
+	if (elf_ex.e_phnum > 65536U / sizeof(struct elf_phdr))
+		goto out;
+	size = elf_ex.e_phnum * sizeof(struct elf_phdr);
 	elf_phdata = (struct elf_phdr *) kmalloc(size, GFP_KERNEL);
 	if (!elf_phdata)
 		goto out;
@@ -472,16 +475,14 @@
 
 	for (i = 0; i < elf_ex.e_phnum; i++) {
 		if (elf_ppnt->p_type == PT_INTERP) {
-			retval = -EINVAL;
-		  	if (elf_interpreter)
-				goto out_free_dentry;
-
 			/* This is the program interpreter used for
 			 * shared libraries - for now assume that this
 			 * is an a.out format binary
 			 */
 
 			retval = -ENOMEM;
+			if (elf_ppnt->p_filesz > PATH_MAX)
+				goto out_free_file;
 			elf_interpreter = (char *) kmalloc(elf_ppnt->p_filesz,
 							   GFP_KERNEL);
 			if (!elf_interpreter)
@@ -536,6 +537,7 @@
 			/* Get the exec headers */
 			interp_ex = *((struct exec *) bprm->buf);
 			interp_elf_ex = *((struct elfhdr *) bprm->buf);
+			break;
 		}
 		elf_ppnt++;
 	}
@@ -805,7 +807,7 @@
 static int load_elf_library(struct file *file)
 {
 	struct elf_phdr *elf_phdata;
-	unsigned long elf_bss = 0, bss, len, k;
+	unsigned long elf_bss, bss, len;
 	int retval, error, i, j;
 	struct elfhdr elf_ex;
 
@@ -825,19 +827,18 @@
 	/* Now read in all of the header information */
 
 	j = sizeof(struct elf_phdr) * elf_ex.e_phnum;
-	if (j > ELF_MIN_ALIGN)
-		goto out;
+	/* j < ELF_MIN_ALIGN because elf_ex.e_phnum <= 2 */
 
 	error = -ENOMEM;
 	elf_phdata = (struct elf_phdr *) kmalloc(j, GFP_KERNEL);
 	if (!elf_phdata)
 		goto out;
 
-	/* N.B. check for error return?? */
-	retval = kernel_read(file, elf_ex.e_phoff, (char *) elf_phdata,
-			   sizeof(struct elf_phdr) * elf_ex.e_phnum);
-
 	error = -ENOEXEC;
+	retval = kernel_read(file, elf_ex.e_phoff, (char *) elf_phdata, j);
+	if (retval != j)
+		goto out_free_ph;
+
 	for (j = 0, i = 0; i<elf_ex.e_phnum; i++)
 		if ((elf_phdata + i)->p_type == PT_LOAD) j++;
 	if (j != 1)
@@ -859,9 +860,7 @@
 	if (error != ELF_PAGESTART(elf_phdata->p_vaddr))
 		goto out_free_ph;
 
-	k = elf_phdata->p_vaddr + elf_phdata->p_filesz;
-	if (k > elf_bss)
-		elf_bss = k;
+	elf_bss = elf_phdata->p_vaddr + elf_phdata->p_filesz;
 	padzero(elf_bss);
 
 	len = ELF_PAGESTART(elf_phdata->p_filesz + elf_phdata->p_vaddr + ELF_MIN_ALIGN - 1);
diff -urN linux-2.4.11-pre4/fs/dquot.c linux/fs/dquot.c
--- linux-2.4.11-pre4/fs/dquot.c	Tue Sep 11 08:50:55 2001
+++ linux/fs/dquot.c	Mon Dec  3 23:16:38 2001
@@ -26,7 +26,7 @@
  *		dquot_incr_...() to calling functions.
  *		invalidate_dquots() now writes modified dquots.
  *		Serialized quota_off() and quota_on() for mount point.
- *		Fixed a few bugs in grow_dquots.
+ *		Fixed a few bugs in grow_dquots().
  *		Fixed deadlock in write_dquot() - we no longer account quotas on
  *		quota files
  *		remove_dquot_ref() moved to inode.c - it now traverses through inodes
@@ -34,13 +34,24 @@
  *		Added check for bogus uid and fixed check for group in quotactl.
  *		Jan Kara, <jack@suse.cz>, sponsored by SuSE CR, 10-11/99
  *
+ *		Used struct list_head instead of own list struct
+ *		Invalidation of dquots with dq_count > 0 no longer possible
+ *		Improved free_dquots list management
+ *		Quota and i_blocks are now updated in one place to avoid races
+ *		Warnings are now delayed so we won't block in critical section
+ *		Write updated not to require dquot lock
+ *		Jan Kara, <jack@suse.cz>, 9/2000
+ *
+ *		Added dynamic quota structure allocation
+ *		Jan Kara <jack@suse.cz> 12/2000
+ *
  * (C) Copyright 1994 - 1997 Marco van Wieringen 
  */
 
 #include <linux/errno.h>
 #include <linux/kernel.h>
+#include <linux/fs.h>
 #include <linux/sched.h>
-
 #include <linux/types.h>
 #include <linux/string.h>
 #include <linux/fcntl.h>
@@ -48,8 +59,6 @@
 #include <linux/tty.h>
 #include <linux/file.h>
 #include <linux/slab.h>
-#include <linux/mount.h>
-#include <linux/smp.h>
 #include <linux/smp_lock.h>
 #include <linux/init.h>
 
@@ -58,9 +67,7 @@
 #define __DQUOT_VERSION__	"dquot_6.4.0"
 
 int nr_dquots, nr_free_dquots;
-int max_dquots = NR_DQUOTS;
 
-static char quotamessage[MAX_QUOTA_MESSAGE];
 static char *quotatypes[] = INITQFNAMES;
 
 static inline struct quota_mount_options *sb_dqopt(struct super_block *sb)
@@ -74,28 +81,41 @@
  * free_dquots, and dquot_hash[] array. A single dquot structure may be
  * on all three lists, depending on its current state.
  *
- * All dquots are placed on the inuse_list when first created, and this
+ * All dquots are placed to the end of inuse_list when first created, and this
  * list is used for the sync and invalidate operations, which must look
  * at every dquot.
  *
  * Unused dquots (dq_count == 0) are added to the free_dquots list when
  * freed, and this list is searched whenever we need an available dquot.
  * Dquots are removed from the list as soon as they are used again, and
- * nr_free_dquots gives the number of dquots on the list.
+ * nr_free_dquots gives the number of dquots on the list. When dquot is
+ * invalidated it's completely released from memory.
  *
  * Dquots with a specific identity (device, type and id) are placed on
  * one of the dquot_hash[] hash chains. The provides an efficient search
- * mechanism to lcoate a specific dquot.
+ * mechanism to locate a specific dquot.
+ */
+
+/*
+ * Note that any operation which operates on dquot data (ie. dq_dqb) mustn't
+ * block while it's updating/reading it. Otherwise races would occur.
+ *
+ * Locked dquots might not be referenced in inodes - operations like
+ * add_dquot_space() does dqduplicate() and would complain. Currently
+ * dquot it locked only once in its existence - when it's being read
+ * to memory on first dqget() and at that time it can't be referenced
+ * from inode. Write operations on dquots don't hold dquot lock as they
+ * copy data to internal buffers before writing anyway and copying as well
+ * as any data update should be atomic. Also nobody can change used
+ * entries in dquot structure as this is done only when quota is destroyed
+ * and invalidate_dquots() waits for dquot to have dq_count == 0.
  */
 
-static struct dquot *inuse_list;
+static LIST_HEAD(inuse_list);
 static LIST_HEAD(free_dquots);
-static struct dquot *dquot_hash[NR_DQHASH];
-static int dquot_updating[NR_DQHASH];
+static struct list_head dquot_hash[NR_DQHASH];
 
 static struct dqstats dqstats;
-static DECLARE_WAIT_QUEUE_HEAD(dquot_wait);
-static DECLARE_WAIT_QUEUE_HEAD(update_wait);
 
 static void dqput(struct dquot *);
 static struct dquot *dqduplicate(struct dquot *);
@@ -123,38 +143,27 @@
 
 static inline void insert_dquot_hash(struct dquot *dquot)
 {
-	struct dquot **htable;
-
-	htable = &dquot_hash[hashfn(dquot->dq_dev, dquot->dq_id, dquot->dq_type)];
-	if ((dquot->dq_hash_next = *htable) != NULL)
-		(*htable)->dq_hash_pprev = &dquot->dq_hash_next;
-	*htable = dquot;
-	dquot->dq_hash_pprev = htable;
+	struct list_head *head = dquot_hash + hashfn(dquot->dq_dev, dquot->dq_id, dquot->dq_type);
+	list_add(&dquot->dq_hash, head);
 }
 
-static inline void hash_dquot(struct dquot *dquot)
+static inline void remove_dquot_hash(struct dquot *dquot)
 {
-	insert_dquot_hash(dquot);
-}
-
-static inline void unhash_dquot(struct dquot *dquot)
-{
-	if (dquot->dq_hash_pprev) {
-		if (dquot->dq_hash_next)
-			dquot->dq_hash_next->dq_hash_pprev = dquot->dq_hash_pprev;
-		*(dquot->dq_hash_pprev) = dquot->dq_hash_next;
-		dquot->dq_hash_pprev = NULL;
-	}
+	list_del(&dquot->dq_hash);
+	INIT_LIST_HEAD(&dquot->dq_hash);
 }
 
 static inline struct dquot *find_dquot(unsigned int hashent, kdev_t dev, unsigned int id, short type)
 {
+	struct list_head *head;
 	struct dquot *dquot;
 
-	for (dquot = dquot_hash[hashent]; dquot; dquot = dquot->dq_hash_next)
+	for (head = dquot_hash[hashent].next; head != dquot_hash+hashent; head = head->next) {
+		dquot = list_entry(head, struct dquot, dq_hash);
 		if (dquot->dq_dev == dev && dquot->dq_id == id && dquot->dq_type == type)
-			break;
-	return dquot;
+			return dquot;
+	}
+	return NODQUOT;
 }
 
 /* Add a dquot to the head of the free list */
@@ -171,6 +180,13 @@
 	nr_free_dquots++;
 }
 
+/* Move dquot to the head of free list (it must be already on it) */
+static inline void move_dquot_head(struct dquot *dquot)
+{
+	list_del(&dquot->dq_free);
+	list_add(&dquot->dq_free, &free_dquots);
+}
+
 static inline void remove_free_dquot(struct dquot *dquot)
 {
 	/* sanity check */
@@ -185,36 +201,30 @@
 
 static inline void put_inuse(struct dquot *dquot)
 {
-	if ((dquot->dq_next = inuse_list) != NULL)
-		inuse_list->dq_pprev = &dquot->dq_next;
-	inuse_list = dquot;
-	dquot->dq_pprev = &inuse_list;
+	/* We add to the back of inuse list so we don't have to restart
+	 * when traversing this list and we block */
+	list_add(&dquot->dq_inuse, inuse_list.prev);
+	nr_dquots++;
 }
 
-#if 0	/* currently not needed */
 static inline void remove_inuse(struct dquot *dquot)
 {
-	if (dquot->dq_pprev) {
-		if (dquot->dq_next)
-			dquot->dq_next->dq_pprev = dquot->dq_pprev;
-		*dquot->dq_pprev = dquot->dq_next;
-		dquot->dq_pprev = NULL;
-	}
+	nr_dquots--;
+	list_del(&dquot->dq_inuse);
 }
-#endif
 
 static void __wait_on_dquot(struct dquot *dquot)
 {
 	DECLARE_WAITQUEUE(wait, current);
 
-	add_wait_queue(&dquot->dq_wait, &wait);
+	add_wait_queue(&dquot->dq_wait_lock, &wait);
 repeat:
 	set_current_state(TASK_UNINTERRUPTIBLE);
 	if (dquot->dq_flags & DQ_LOCKED) {
 		schedule();
 		goto repeat;
 	}
-	remove_wait_queue(&dquot->dq_wait, &wait);
+	remove_wait_queue(&dquot->dq_wait_lock, &wait);
 	current->state = TASK_RUNNING;
 }
 
@@ -233,7 +243,22 @@
 static inline void unlock_dquot(struct dquot *dquot)
 {
 	dquot->dq_flags &= ~DQ_LOCKED;
-	wake_up(&dquot->dq_wait);
+	wake_up(&dquot->dq_wait_lock);
+}
+
+static void __wait_dquot_unused(struct dquot *dquot)
+{
+	DECLARE_WAITQUEUE(wait, current);
+
+	add_wait_queue(&dquot->dq_wait_free, &wait);
+repeat:
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	if (dquot->dq_count) {
+		schedule();
+		goto repeat;
+	}
+	remove_wait_queue(&dquot->dq_wait_free, &wait);
+	current->state = TASK_RUNNING;
 }
 
 /*
@@ -247,12 +272,8 @@
 	loff_t offset;
 	ssize_t ret;
 	struct semaphore *sem = &dquot->dq_sb->s_dquot.dqio_sem;
+	struct dqblk dqbuf;
 
-	lock_dquot(dquot);
-	if (!dquot->dq_sb) {	/* Invalidated quota? */
-		unlock_dquot(dquot);
-		return;
-	}
 	down(sem);
 	filp = dquot->dq_sb->s_dquot.files[type];
 	offset = dqoff(dquot->dq_id);
@@ -263,10 +284,11 @@
 	 * Note: clear the DQ_MOD flag unconditionally,
 	 * so we don't loop forever on failure.
 	 */
+	memcpy(&dqbuf, &dquot->dq_dqb, sizeof(struct dqblk));
 	dquot->dq_flags &= ~DQ_MOD;
 	ret = 0;
 	if (filp)
-		ret = filp->f_op->write(filp, (char *)&dquot->dq_dqb, 
+		ret = filp->f_op->write(filp, (char *)&dqbuf, 
 					sizeof(struct dqblk), &offset);
 	if (ret != sizeof(struct dqblk))
 		printk(KERN_WARNING "VFS: dquota write failed on dev %s\n",
@@ -274,8 +296,6 @@
 
 	set_fs(fs);
 	up(sem);
-	unlock_dquot(dquot);
-
 	dqstats.writes++;
 }
 
@@ -318,96 +338,99 @@
 void clear_dquot(struct dquot *dquot)
 {
 	/* unhash it first */
-        unhash_dquot(dquot);
-        dquot->dq_sb = NULL;
-        dquot->dq_flags = 0;
-        dquot->dq_referenced = 0;
-        memset(&dquot->dq_dqb, 0, sizeof(struct dqblk));
+	remove_dquot_hash(dquot);
+	dquot->dq_sb = NULL;
+	dquot->dq_id = 0;
+	dquot->dq_dev = NODEV;
+	dquot->dq_type = -1;
+	dquot->dq_flags = 0;
+	dquot->dq_referenced = 0;
+	memset(&dquot->dq_dqb, 0, sizeof(struct dqblk));
 }
 
+/* Invalidate all dquots on the list, wait for all users. Note that this function is called
+ * after quota is disabled so no new quota might be created. As we only insert to the end of
+ * inuse list, we don't have to restart searching... */
 static void invalidate_dquots(struct super_block *sb, short type)
 {
-	struct dquot *dquot, *next;
-	int need_restart;
+	struct dquot *dquot;
+	struct list_head *head;
 
 restart:
-	next = inuse_list;	/* Here it is better. Otherwise the restart doesn't have any sense ;-) */
-	need_restart = 0;
-	while ((dquot = next) != NULL) {
-		next = dquot->dq_next;
+	for (head = inuse_list.next; head != &inuse_list; head = head->next) {
+		dquot = list_entry(head, struct dquot, dq_inuse);
 		if (dquot->dq_sb != sb)
 			continue;
 		if (dquot->dq_type != type)
 			continue;
-		if (dquot->dq_flags & DQ_LOCKED) {
-			__wait_on_dquot(dquot);
-
-			/* Set the flag for another pass. */
-			need_restart = 1;
+		if (dquot->dq_count)
 			/*
-			 * Make sure it's still the same dquot.
+			 *  Wait for any users of quota. As we have already cleared the flags in
+			 *  superblock and cleared all pointers from inodes we are assured
+			 *  that there will be no new users of this quota.
 			 */
-			if (dquot->dq_sb != sb)
-				continue;
-			if (dquot->dq_type != type)
-				continue;
-		}
-		/*
-		 *  Because inodes needn't to be the only holders of dquot
-		 *  the quota needn't to be written to disk. So we write it
-		 *  ourselves before discarding the data just for sure...
-		 */
-		if (dquot->dq_flags & DQ_MOD && dquot->dq_sb)
-		{
-			write_dquot(dquot);
-			need_restart = 1;	/* We slept on IO */
-		}
-		clear_dquot(dquot);
-	}
-	/*
-	 * If anything blocked, restart the operation
-	 * to ensure we don't miss any dquots.
-	 */ 
-	if (need_restart)
+			__wait_dquot_unused(dquot);
+		/* Quota now have no users and it has been written on last dqput() */
+		remove_dquot_hash(dquot);
+		remove_free_dquot(dquot);
+		remove_inuse(dquot);
+		kmem_cache_free(dquot_cachep, dquot);
 		goto restart;
+	}
 }
 
 int sync_dquots(kdev_t dev, short type)
 {
-	struct dquot *dquot, *next, *ddquot;
-	int need_restart;
+	struct list_head *head;
+	struct dquot *dquot;
 
 restart:
-	next = inuse_list;
-	need_restart = 0;
-	while ((dquot = next) != NULL) {
-		next = dquot->dq_next;
+	for (head = inuse_list.next; head != &inuse_list; head = head->next) {
+		dquot = list_entry(head, struct dquot, dq_inuse);
 		if (dev && dquot->dq_dev != dev)
 			continue;
                 if (type != -1 && dquot->dq_type != type)
 			continue;
 		if (!dquot->dq_sb)	/* Invalidated? */
 			continue;
-		if (!(dquot->dq_flags & (DQ_LOCKED | DQ_MOD)))
+		if (!(dquot->dq_flags & (DQ_MOD | DQ_LOCKED)))
 			continue;
+		/* Raise use count so quota won't be invalidated. We can't use dqduplicate() as it does too many tests */
+		dquot->dq_count++;
+		if (dquot->dq_flags & DQ_LOCKED)
+			wait_on_dquot(dquot);
+		if (dquot->dq_flags & DQ_MOD)
+			write_dquot(dquot);
+		dqput(dquot);
+		goto restart;
+	}
+	dqstats.syncs++;
+	return 0;
+}
 
-		if ((ddquot = dqduplicate(dquot)) == NODQUOT)
-			continue;
-		if (ddquot->dq_flags & DQ_MOD)
-			write_dquot(ddquot);
-		dqput(ddquot);
-		/* Set the flag for another pass. */
-		need_restart = 1;
+/* Free unused dquots from cache */
+static void prune_dqcache(int count)
+{
+	struct list_head *head;
+	struct dquot *dquot;
+
+	head = free_dquots.prev;
+	while (head != &free_dquots && count) {
+		dquot = list_entry(head, struct dquot, dq_free);
+		remove_dquot_hash(dquot);
+		remove_free_dquot(dquot);
+		remove_inuse(dquot);
+		kmem_cache_free(dquot_cachep, dquot);
+		count--;
+		head = free_dquots.prev;
 	}
-	/*
-	 * If anything blocked, restart the operation
-	 * to ensure we don't miss any dquots.
-	 */ 
-	if (need_restart)
-		goto restart;
+}
 
-	dqstats.syncs++;
-	return(0);
+int shrink_dqcache_memory(int priority, unsigned int gfp_mask)
+{
+	prune_dqcache(nr_free_dquots / (priority + 1));
+	kmem_cache_shrink(dquot_cachep);
+	return 0;
 }
 
 /* NOTE: If you change this function please check whether dqput_blocks() works right... */
@@ -423,24 +446,14 @@
 		return;
 	}
 
-	/*
-	 * If the dq_sb pointer isn't initialized this entry needs no
-	 * checking and doesn't need to be written. It's just an empty
-	 * dquot that is put back on to the freelist.
-	 */
-	if (dquot->dq_sb)
-		dqstats.drops++;
+	dqstats.drops++;
 we_slept:
 	if (dquot->dq_count > 1) {
 		/* We have more than one user... We can simply decrement use count */
 		dquot->dq_count--;
 		return;
 	}
-	if (dquot->dq_flags & DQ_LOCKED) {
-		printk(KERN_ERR "VFS: Locked quota to be put on the free list.\n");
-		dquot->dq_flags &= ~DQ_LOCKED;
-	}
-	if (dquot->dq_sb && dquot->dq_flags & DQ_MOD) {
+	if (dquot->dq_flags & DQ_MOD) {
 		write_dquot(dquot);
 		goto we_slept;
 	}
@@ -452,130 +465,49 @@
 		return;
 	}
 	dquot->dq_count--;
-	dquot->dq_flags &= ~DQ_MOD;	/* Modified flag has no sense on free list */
 	/* Place at end of LRU free queue */
 	put_dquot_last(dquot);
-	wake_up(&dquot_wait);
-}
-
-static int grow_dquots(void)
-{
-	struct dquot *dquot;
-	int cnt = 0;
-
-	while (cnt < 32) {
-		dquot = kmem_cache_alloc(dquot_cachep, SLAB_KERNEL);
-		if(!dquot)
-			return cnt;
-
-		nr_dquots++;
-		memset((caddr_t)dquot, 0, sizeof(struct dquot));
-		init_waitqueue_head(&dquot->dq_wait);
-		/* all dquots go on the inuse_list */
-		put_inuse(dquot);
-		put_dquot_head(dquot);
-		cnt++;
-	}
-	return cnt;
-}
-
-static struct dquot *find_best_candidate_weighted(void)
-{
-	struct list_head *tmp = &free_dquots;
-	struct dquot *dquot, *best = NULL;
-	unsigned long myscore, bestscore = ~0U;
-	int limit = (nr_free_dquots > 128) ? nr_free_dquots >> 2 : 32;
-
-	while ((tmp = tmp->next) != &free_dquots && --limit) {
-		dquot = list_entry(tmp, struct dquot, dq_free);
-		/* This should never happen... */
-		if (dquot->dq_flags & (DQ_LOCKED | DQ_MOD))
-			continue;
-		myscore = dquot->dq_referenced;
-		if (myscore < bestscore) {
-			bestscore = myscore;
-			best = dquot;
-		}
-	}
-	return best;
-}
-
-static inline struct dquot *find_best_free(void)
-{
-	struct list_head *tmp = &free_dquots;
-	struct dquot *dquot;
-	int limit = (nr_free_dquots > 1024) ? nr_free_dquots >> 5 : 32;
-
-	while ((tmp = tmp->next) != &free_dquots && --limit) {
-		dquot = list_entry(tmp, struct dquot, dq_free);
-		if (dquot->dq_referenced == 0)
-			return dquot;
-	}
-	return NULL;
+	wake_up(&dquot->dq_wait_free);
 }
 
 struct dquot *get_empty_dquot(void)
 {
 	struct dquot *dquot;
-	int shrink = 8;	/* Number of times we should try to shrink dcache and icache */
 
-repeat:
-	dquot = find_best_free();
-	if (!dquot)
-		goto pressure;
-got_it:
-	/* Sanity checks */
-	if (dquot->dq_flags & DQ_LOCKED)
-		printk(KERN_ERR "VFS: Locked dquot on the free list\n");
-	if (dquot->dq_count != 0)
-		printk(KERN_ERR "VFS: free dquot count=%d\n", dquot->dq_count);
+	dquot = kmem_cache_alloc(dquot_cachep, SLAB_KERNEL);
+	if(!dquot)
+		return NODQUOT;
 
-	remove_free_dquot(dquot);
+	memset((caddr_t)dquot, 0, sizeof(struct dquot));
+	init_waitqueue_head(&dquot->dq_wait_free);
+	init_waitqueue_head(&dquot->dq_wait_lock);
+	INIT_LIST_HEAD(&dquot->dq_free);
+	INIT_LIST_HEAD(&dquot->dq_inuse);
+	INIT_LIST_HEAD(&dquot->dq_hash);
 	dquot->dq_count = 1;
-	/* unhash and selectively clear the structure */
-	clear_dquot(dquot);
-	return dquot;
+	/* all dquots go on the inuse_list */
+	put_inuse(dquot);
 
-pressure:
-	if (nr_dquots < max_dquots)
-		if (grow_dquots())
-			goto repeat;
-
-	dquot = find_best_candidate_weighted();
-	if (dquot)
-		goto got_it;
-	/*
-	 * Try pruning the dcache to free up some dquots ...
-	 */
-	if (shrink) {
-		printk(KERN_DEBUG "get_empty_dquot: pruning dcache and icache\n");
-		prune_dcache(128);
-		prune_icache(128);
-		shrink--;
-		goto repeat;
-	}
-
-	printk("VFS: No free dquots, contact mvw@planets.elm.net\n");
-	sleep_on(&dquot_wait);
-	goto repeat;
+	return dquot;
 }
 
 static struct dquot *dqget(struct super_block *sb, unsigned int id, short type)
 {
 	unsigned int hashent = hashfn(sb->s_dev, id, type);
-	struct dquot *dquot, *empty = NULL;
+	struct dquot *dquot, *empty = NODQUOT;
 	struct quota_mount_options *dqopt = sb_dqopt(sb);
 
-        if (!is_enabled(dqopt, type))
-                return(NODQUOT);
-
 we_slept:
-	if ((dquot = find_dquot(hashent, sb->s_dev, id, type)) == NULL) {
-		if (empty == NULL) {
-			dquot_updating[hashent]++;
-			empty = get_empty_dquot();
-			if (!--dquot_updating[hashent])
-				wake_up(&update_wait);
+        if (!is_enabled(dqopt, type)) {
+		if (empty)
+			dqput(empty);
+                return NODQUOT;
+	}
+
+	if ((dquot = find_dquot(hashent, sb->s_dev, id, type)) == NODQUOT) {
+		if (empty == NODQUOT) {
+			if ((empty = get_empty_dquot()) == NODQUOT)
+				schedule();	/* Try to wait for a moment... */
 			goto we_slept;
 		}
 		dquot = empty;
@@ -584,25 +516,19 @@
         	dquot->dq_dev = sb->s_dev;
         	dquot->dq_sb = sb;
 		/* hash it first so it can be found */
-		hash_dquot(dquot);
+		insert_dquot_hash(dquot);
         	read_dquot(dquot);
 	} else {
-		if (!dquot->dq_count++) {
+		if (!dquot->dq_count++)
 			remove_free_dquot(dquot);
-		} else
-			dqstats.cache_hits++;
+		dqstats.cache_hits++;
 		wait_on_dquot(dquot);
 		if (empty)
 			dqput(empty);
 	}
 
-	while (dquot_updating[hashent])
-		sleep_on(&update_wait);
-
 	if (!dquot->dq_sb) {	/* Has somebody invalidated entry under us? */
-		/*
-		 *  Do it as if the quota was invalidated before we started
-		 */
+		printk(KERN_ERR "VFS: dqget(): Quota invalidated in dqget()!\n");
 		dqput(dquot);
 		return NODQUOT;
 	}
@@ -614,14 +540,16 @@
 
 static struct dquot *dqduplicate(struct dquot *dquot)
 {
-	if (dquot == NODQUOT || !dquot->dq_sb)
+	if (dquot == NODQUOT)
 		return NODQUOT;
 	dquot->dq_count++;
-	wait_on_dquot(dquot);
 	if (!dquot->dq_sb) {
+		printk(KERN_ERR "VFS: dqduplicate(): Invalidated quota to be duplicated!\n");
 		dquot->dq_count--;
 		return NODQUOT;
 	}
+	if (dquot->dq_flags & DQ_LOCKED)
+		printk(KERN_ERR "VFS: dqduplicate(): Locked quota to be duplicated!\n");
 	dquot->dq_referenced++;
 	dqstats.lookups++;
 	return dquot;
@@ -695,9 +623,9 @@
 				printk(KERN_WARNING "VFS: Adding dquot with dq_count %d to dispose list.\n", dquot->dq_count);
 			list_add(&dquot->dq_free, tofree_head);	/* As dquot must have currently users it can't be on the free list... */
 			return 1;
-		} else {
-			dqput(dquot);   /* We have guaranteed we won't block */
 		}
+		else
+			dqput(dquot);   /* We have guaranteed we won't block */
 	}
 	return 0;
 }
@@ -765,14 +693,61 @@
 	return 0;
 }
 
-static void print_warning(struct dquot *dquot, int flag, const char *fmtstr)
-{
+/* Values of warnings */
+#define NOWARN 0
+#define IHARDWARN 1
+#define ISOFTLONGWARN 2
+#define ISOFTWARN 3
+#define BHARDWARN 4
+#define BSOFTLONGWARN 5
+#define BSOFTWARN 6
+
+/* Print warning to user which exceeded quota */
+static void print_warning(struct dquot *dquot, const char warntype)
+{
+	char *msg = NULL;
+	int flag = (warntype == BHARDWARN || warntype == BSOFTLONGWARN) ? DQ_BLKS :
+	  ((warntype == IHARDWARN || warntype == ISOFTLONGWARN) ? DQ_INODES : 0);
+
 	if (!need_print_warning(dquot, flag))
 		return;
-	sprintf(quotamessage, fmtstr,
-		bdevname(dquot->dq_sb->s_dev), quotatypes[dquot->dq_type]);
-	tty_write_message(current->tty, quotamessage);
 	dquot->dq_flags |= flag;
+	tty_write_message(current->tty, (char *)bdevname(dquot->dq_sb->s_dev));
+	if (warntype == ISOFTWARN || warntype == BSOFTWARN)
+		tty_write_message(current->tty, ": warning, ");
+	else
+		tty_write_message(current->tty, ": write failed, ");
+	tty_write_message(current->tty, quotatypes[dquot->dq_type]);
+	switch (warntype) {
+		case IHARDWARN:
+			msg = " file limit reached.\n";
+			break;
+		case ISOFTLONGWARN:
+			msg = " file quota exceeded too long.\n";
+			break;
+		case ISOFTWARN:
+			msg = " file quota exceeded.\n";
+			break;
+		case BHARDWARN:
+			msg = " block limit reached.\n";
+			break;
+		case BSOFTLONGWARN:
+			msg = " block quota exceeded too long.\n";
+			break;
+		case BSOFTWARN:
+			msg = " block quota exceeded.\n";
+			break;
+	}
+	tty_write_message(current->tty, msg);
+}
+
+static inline void flush_warnings(struct dquot **dquots, char *warntype)
+{
+	int i;
+
+	for (i = 0; i < MAXQUOTAS; i++)
+		if (dquots[i] != NODQUOT && warntype[i] != NOWARN)
+			print_warning(dquots[i], warntype[i]);
 }
 
 static inline char ignore_hardlimit(struct dquot *dquot)
@@ -780,15 +755,16 @@
 	return capable(CAP_SYS_RESOURCE) && !dquot->dq_sb->s_dquot.rsquash[dquot->dq_type];
 }
 
-static int check_idq(struct dquot *dquot, u_long inodes)
+static int check_idq(struct dquot *dquot, ulong inodes, char *warntype)
 {
+	*warntype = NOWARN;
 	if (inodes <= 0 || dquot->dq_flags & DQ_FAKE)
 		return QUOTA_OK;
 
 	if (dquot->dq_ihardlimit &&
 	   (dquot->dq_curinodes + inodes) > dquot->dq_ihardlimit &&
             !ignore_hardlimit(dquot)) {
-		print_warning(dquot, DQ_INODES, "%s: write failed, %s file limit reached\n");
+		*warntype = IHARDWARN;
 		return NO_QUOTA;
 	}
 
@@ -796,22 +772,23 @@
 	   (dquot->dq_curinodes + inodes) > dquot->dq_isoftlimit &&
 	    dquot->dq_itime && CURRENT_TIME >= dquot->dq_itime &&
             !ignore_hardlimit(dquot)) {
-		print_warning(dquot, DQ_INODES, "%s: warning, %s file quota exceeded too long.\n");
+		*warntype = ISOFTLONGWARN;
 		return NO_QUOTA;
 	}
 
 	if (dquot->dq_isoftlimit &&
 	   (dquot->dq_curinodes + inodes) > dquot->dq_isoftlimit &&
 	    dquot->dq_itime == 0) {
-		print_warning(dquot, 0, "%s: warning, %s file quota exceeded\n");
+		*warntype = ISOFTWARN;
 		dquot->dq_itime = CURRENT_TIME + dquot->dq_sb->s_dquot.inode_expire[dquot->dq_type];
 	}
 
 	return QUOTA_OK;
 }
 
-static int check_bdq(struct dquot *dquot, u_long blocks, char prealloc)
+static int check_bdq(struct dquot *dquot, ulong blocks, char prealloc, char *warntype)
 {
+	*warntype = 0;
 	if (blocks <= 0 || dquot->dq_flags & DQ_FAKE)
 		return QUOTA_OK;
 
@@ -819,7 +796,7 @@
 	   (dquot->dq_curblocks + blocks) > dquot->dq_bhardlimit &&
             !ignore_hardlimit(dquot)) {
 		if (!prealloc)
-			print_warning(dquot, DQ_BLKS, "%s: write failed, %s disk limit reached.\n");
+			*warntype = BHARDWARN;
 		return NO_QUOTA;
 	}
 
@@ -828,7 +805,7 @@
 	    dquot->dq_btime && CURRENT_TIME >= dquot->dq_btime &&
             !ignore_hardlimit(dquot)) {
 		if (!prealloc)
-			print_warning(dquot, DQ_BLKS, "%s: write failed, %s disk quota exceeded too long.\n");
+			*warntype = BSOFTLONGWARN;
 		return NO_QUOTA;
 	}
 
@@ -836,7 +813,7 @@
 	   (dquot->dq_curblocks + blocks) > dquot->dq_bsoftlimit &&
 	    dquot->dq_btime == 0) {
 		if (!prealloc) {
-			print_warning(dquot, 0, "%s: warning, %s disk quota exceeded\n");
+			*warntype = BSOFTWARN;
 			dquot->dq_btime = CURRENT_TIME + dquot->dq_sb->s_dquot.block_expire[dquot->dq_type];
 		}
 		else
@@ -860,18 +837,11 @@
 	int error = -EFAULT;
 	struct dqblk dq_dqblk;
 
-	if (dqblk == (struct dqblk *)NULL)
+	if (copy_from_user(&dq_dqblk, dqblk, sizeof(struct dqblk)))
 		return error;
 
-	if (flags & QUOTA_SYSCALL) {
-		if (copy_from_user(&dq_dqblk, dqblk, sizeof(struct dqblk)))
-			return(error);
-	} else
-		memcpy((caddr_t)&dq_dqblk, (caddr_t)dqblk, sizeof(struct dqblk));
-
 	if (sb && (dquot = dqget(sb, id, type)) != NODQUOT) {
-		lock_dquot(dquot);
-
+		/* We can't block while changing quota structure... */
 		if (id > 0 && ((flags & SET_QUOTA) || (flags & SET_QLIMIT))) {
 			dquot->dq_bhardlimit = dq_dqblk.dqb_bhardlimit;
 			dquot->dq_bsoftlimit = dq_dqblk.dqb_bsoftlimit;
@@ -908,15 +878,15 @@
 			dquot->dq_flags &= ~DQ_FAKE;
 
 		dquot->dq_flags |= DQ_MOD;
-		unlock_dquot(dquot);
 		dqput(dquot);
 	}
-	return(0);
+	return 0;
 }
 
 static int get_quota(struct super_block *sb, int id, short type, struct dqblk *dqblk)
 {
 	struct dquot *dquot;
+	struct dqblk data;
 	int error = -ESRCH;
 
 	if (!sb || !sb_has_quota_enabled(sb, type))
@@ -925,12 +895,11 @@
 	if (dquot == NODQUOT)
 		goto out;
 
-	lock_dquot(dquot);	/* We must protect against invalidating the quota */
+	memcpy(&data, &dquot->dq_dqb, sizeof(struct dqblk));        /* We copy data to preserve them from changing */
+	dqput(dquot);
 	error = -EFAULT;
-	if (dqblk && !copy_to_user(dqblk, &dquot->dq_dqb, sizeof(struct dqblk)))
+	if (dqblk && !copy_to_user(dqblk, &data, sizeof(struct dqblk)))
 		error = 0;
-	unlock_dquot(dquot);
-	dqput(dquot);
 out:
 	return error;
 }
@@ -965,6 +934,7 @@
 	return error;
 }
 
+#if 0	/* We are not going to support filesystems without i_blocks... */
 /*
  * This is a simple algorithm that calculates the size of a file in blocks.
  * This is only used on filesystems that do not have an i_blocks count.
@@ -988,6 +958,7 @@
 	}
 	return blocks;
 }
+#endif
 
 /*
  * Externally referenced functions through dquot_operations in inode.
@@ -996,22 +967,19 @@
  */
 void dquot_initialize(struct inode *inode, short type)
 {
-	struct dquot *dquot;
+	struct dquot *dquot[MAXQUOTAS];
 	unsigned int id = 0;
 	short cnt;
 
-	lock_kernel();
-	if (IS_NOQUOTA(inode)) {
-		unlock_kernel();
+	if (IS_NOQUOTA(inode))
 		return;
-	}
+	/* Build list of quotas to initialize... We can block here */
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+		dquot[cnt] = NODQUOT;
 		if (type != -1 && cnt != type)
 			continue;
-
 		if (!sb_has_quota_enabled(inode->i_sb, cnt))
 			continue;
-
 		if (inode->i_dquot[cnt] == NODQUOT) {
 			switch (cnt) {
 				case USRQUOTA:
@@ -1021,18 +989,22 @@
 					id = inode->i_gid;
 					break;
 			}
-			dquot = dqget(inode->i_sb, id, cnt);
-			if (dquot == NODQUOT)
-				continue;
-			if (inode->i_dquot[cnt] != NODQUOT) {
-				dqput(dquot);
-				continue;
-			} 
-			inode->i_dquot[cnt] = dquot;
-			inode->i_flags |= S_QUOTA;
+			dquot[cnt] = dqget(inode->i_sb, id, cnt);
 		}
 	}
-	unlock_kernel();
+	/* NOBLOCK START: Here we shouldn't block */
+	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+		if (dquot[cnt] == NODQUOT || !sb_has_quota_enabled(inode->i_sb, cnt) || inode->i_dquot[cnt] != NODQUOT)
+			continue;
+		inode->i_dquot[cnt] = dquot[cnt];
+		dquot[cnt] = NODQUOT;
+		inode->i_flags |= S_QUOTA;
+	}
+	/* NOBLOCK END */
+	/* Put quotas which we didn't use */
+	for (cnt = 0; cnt < MAXQUOTAS; cnt++)
+		if (dquot[cnt] != NODQUOT)
+			dqput(dquot[cnt]);
 }
 
 /*
@@ -1045,7 +1017,6 @@
 	struct dquot *dquot;
 	short cnt;
 
-	lock_kernel();
 	inode->i_flags &= ~S_QUOTA;
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
 		if (inode->i_dquot[cnt] == NODQUOT)
@@ -1054,229 +1025,180 @@
 		inode->i_dquot[cnt] = NODQUOT;
 		dqput(dquot);
 	}
-	unlock_kernel();
 }
 
 /*
- * Note: this is a blocking operation.
+ * This operation can block, but only after everything is updated
  */
-int dquot_alloc_block(const struct inode *inode, unsigned long number, char warn)
+int dquot_alloc_block(struct inode *inode, unsigned long number, char warn)
 {
-	int cnt;
+	int cnt, ret = NO_QUOTA;
 	struct dquot *dquot[MAXQUOTAS];
+	char warntype[MAXQUOTAS];
 
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+		dquot[cnt] = NODQUOT;
+		warntype[cnt] = NOWARN;
+	}
+	/* NOBLOCK Start */
+	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
 		dquot[cnt] = dqduplicate(inode->i_dquot[cnt]);
 		if (dquot[cnt] == NODQUOT)
 			continue;
-		lock_dquot(dquot[cnt]);
-		if (check_bdq(dquot[cnt], number, warn))
-			goto put_all;
+		if (check_bdq(dquot[cnt], number, warn, warntype+cnt) == NO_QUOTA)
+			goto warn_put_all;
 	}
-
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
 		if (dquot[cnt] == NODQUOT)
 			continue;
 		dquot_incr_blocks(dquot[cnt], number);
-		unlock_dquot(dquot[cnt]);
-		dqput(dquot[cnt]);
 	}
-
-	return QUOTA_OK;
-put_all:
-	for (; cnt >= 0; cnt--) {
-		if (dquot[cnt] == NODQUOT)
-			continue;
-		unlock_dquot(dquot[cnt]);
-		dqput(dquot[cnt]);
-	}
-	return NO_QUOTA;
+	inode->i_blocks += number << (BLOCK_SIZE_BITS - 9);
+	/* NOBLOCK End */
+	ret = QUOTA_OK;
+warn_put_all:
+	flush_warnings(dquot, warntype);
+	for (cnt = 0; cnt < MAXQUOTAS; cnt++)
+		if (dquot[cnt] != NODQUOT)
+			dqput(dquot[cnt]);
+	return ret;
 }
 
 /*
- * Note: this is a blocking operation.
+ * This operation can block, but only after everything is updated
  */
 int dquot_alloc_inode(const struct inode *inode, unsigned long number)
 {
-	int cnt;
+	int cnt, ret = NO_QUOTA;
 	struct dquot *dquot[MAXQUOTAS];
+	char warntype[MAXQUOTAS];
 
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+		dquot[cnt] = NODQUOT;
+		warntype[cnt] = NOWARN;
+	}
+	/* NOBLOCK Start */
+	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
 		dquot[cnt] = dqduplicate(inode -> i_dquot[cnt]);
 		if (dquot[cnt] == NODQUOT)
 			continue;
-		lock_dquot(dquot[cnt]);
-		if (check_idq(dquot[cnt], number))
-			goto put_all;
+		if (check_idq(dquot[cnt], number, warntype+cnt) == NO_QUOTA)
+			goto warn_put_all;
 	}
 
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
 		if (dquot[cnt] == NODQUOT)
 			continue;
 		dquot_incr_inodes(dquot[cnt], number);
-		unlock_dquot(dquot[cnt]);
-		dqput(dquot[cnt]);
 	}
-
-	return QUOTA_OK;
-put_all:
-	for (; cnt >= 0; cnt--) {
-		if (dquot[cnt] == NODQUOT)
-			continue;
-		unlock_dquot(dquot[cnt]);
-		dqput(dquot[cnt]);
-	}
-	return NO_QUOTA;
+	/* NOBLOCK End */
+	ret = QUOTA_OK;
+warn_put_all:
+	flush_warnings(dquot, warntype);
+	for (cnt = 0; cnt < MAXQUOTAS; cnt++)
+		if (dquot[cnt] != NODQUOT)
+			dqput(dquot[cnt]);
+	return ret;
 }
 
 /*
- * Note: this is a blocking operation.
+ * This is a non-blocking operation.
  */
-void dquot_free_block(const struct inode *inode, unsigned long number)
+void dquot_free_block(struct inode *inode, unsigned long number)
 {
 	unsigned short cnt;
 	struct dquot *dquot;
 
+	/* NOBLOCK Start */
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
-		dquot = inode->i_dquot[cnt];
+		dquot = dqduplicate(inode->i_dquot[cnt]);
 		if (dquot == NODQUOT)
 			continue;
-		wait_on_dquot(dquot);
 		dquot_decr_blocks(dquot, number);
+		dqput(dquot);
 	}
+	inode->i_blocks -= number << (BLOCK_SIZE_BITS - 9);
+	/* NOBLOCK End */
 }
 
 /*
- * Note: this is a blocking operation.
+ * This is a non-blocking operation.
  */
 void dquot_free_inode(const struct inode *inode, unsigned long number)
 {
 	unsigned short cnt;
 	struct dquot *dquot;
 
+	/* NOBLOCK Start */
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
-		dquot = inode->i_dquot[cnt];
+		dquot = dqduplicate(inode->i_dquot[cnt]);
 		if (dquot == NODQUOT)
 			continue;
-		wait_on_dquot(dquot);
 		dquot_decr_inodes(dquot, number);
+		dqput(dquot);
 	}
+	/* NOBLOCK End */
 }
 
 /*
  * Transfer the number of inode and blocks from one diskquota to an other.
  *
- * Note: this is a blocking operation.
+ * This operation can block, but only after everything is updated
  */
-int dquot_transfer(struct dentry *dentry, struct iattr *iattr)
+int dquot_transfer(struct inode *inode, struct iattr *iattr)
 {
-	struct inode *inode = dentry -> d_inode;
 	unsigned long blocks;
 	struct dquot *transfer_from[MAXQUOTAS];
 	struct dquot *transfer_to[MAXQUOTAS];
-	short cnt, disc;
-	int error = -EDQUOT;
-
-	if (!inode)
-		return -ENOENT;
-	/* Arguably we could consider that as error, but... no fs - no quota */
-	if (!inode->i_sb)
-		return 0;
+	int cnt, ret = NO_QUOTA, chuid = (iattr->ia_valid & ATTR_UID) && inode->i_uid != iattr->ia_uid,
+	    chgid = (iattr->ia_valid & ATTR_GID) && inode->i_gid != iattr->ia_gid;
+	char warntype[MAXQUOTAS];
 
-	lock_kernel();
-	/*
-	 * Build the transfer_from and transfer_to lists and check quotas to see
-	 * if operation is permitted.
-	 */
+	/* Clear the arrays */
+	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+		transfer_to[cnt] = transfer_from[cnt] = NODQUOT;
+		warntype[cnt] = NOWARN;
+	}
+	/* First build the transfer_to list - here we can block on reading of dquots... */
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
-		transfer_from[cnt] = NODQUOT;
-		transfer_to[cnt] = NODQUOT;
-
 		if (!sb_has_quota_enabled(inode->i_sb, cnt))
 			continue;
-
 		switch (cnt) {
 			case USRQUOTA:
-				if (inode->i_uid == iattr->ia_uid)
+				if (!chuid)
 					continue;
-				/* We can get transfer_from from inode, can't we? */
-				transfer_from[cnt] = dqget(inode->i_sb, inode->i_uid, cnt);
 				transfer_to[cnt] = dqget(inode->i_sb, iattr->ia_uid, cnt);
 				break;
 			case GRPQUOTA:
-				if (inode->i_gid == iattr->ia_gid)
+				if (!chgid)
 					continue;
-				transfer_from[cnt] = dqget(inode->i_sb, inode->i_gid, cnt);
 				transfer_to[cnt] = dqget(inode->i_sb, iattr->ia_gid, cnt);
 				break;
 		}
-
-		/* Something bad (eg. quotaoff) happened while we were sleeping? */
-		if (transfer_from[cnt] == NODQUOT || transfer_to[cnt] == NODQUOT)
-		{
-			if (transfer_from[cnt] != NODQUOT) {
-				dqput(transfer_from[cnt]);
-				transfer_from[cnt] = NODQUOT;
-			}
-			if (transfer_to[cnt] != NODQUOT) {
-				dqput(transfer_to[cnt]);
-				transfer_to[cnt] = NODQUOT;
-			}
-			continue;
-		}
-		/*
-		 *  We have to lock the quotas to prevent races...
-		 */
-		if (transfer_from[cnt] < transfer_to[cnt])
-		{
-                	lock_dquot(transfer_from[cnt]);
-			lock_dquot(transfer_to[cnt]);
-		}
-		else
-		{
-			lock_dquot(transfer_to[cnt]);
-			lock_dquot(transfer_from[cnt]);
-		}
-
-		/*
-		 * The entries might got invalidated while locking. The second
-		 * dqget() could block and so the first structure might got
-		 * invalidated or locked...
-		 */
-		if (!transfer_to[cnt]->dq_sb || !transfer_from[cnt]->dq_sb) {
-			cnt++;
-			goto put_all;
-		}
 	}
-
-	/*
-	 * Find out if this filesystem uses i_blocks.
-	 */
-	if (!inode->i_sb->s_blocksize)
-		blocks = isize_to_blocks(inode->i_size, BLOCK_SIZE_BITS);
-	else
-		blocks = (inode->i_blocks >> 1);
+	/* NOBLOCK START: From now on we shouldn't block */
+	blocks = (inode->i_blocks >> 1);
+	/* Build the transfer_from list and check the limits */
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
-		if (!transfer_to[cnt])
+		/* The second test can fail when quotaoff is in progress... */
+		if (transfer_to[cnt] == NODQUOT || !sb_has_quota_enabled(inode->i_sb, cnt))
 			continue;
-		if (check_idq(transfer_to[cnt], 1) == NO_QUOTA ||
-		    check_bdq(transfer_to[cnt], blocks, 0) == NO_QUOTA) {
-			cnt = MAXQUOTAS;
-			goto put_all;
-		}
+		transfer_from[cnt] = dqduplicate(inode->i_dquot[cnt]);
+		if (transfer_from[cnt] == NODQUOT)	/* Can happen on quotafiles (quota isn't initialized on them)... */
+			continue;
+		if (check_idq(transfer_to[cnt], 1, warntype+cnt) == NO_QUOTA ||
+		    check_bdq(transfer_to[cnt], blocks, 0, warntype+cnt) == NO_QUOTA)
+			goto warn_put_all;
 	}
 
-	if ((error = notify_change(dentry, iattr)))
-		goto put_all; 
 	/*
-	 * Finally perform the needed transfer from transfer_from to transfer_to,
-	 * and release any pointers to dquots not needed anymore.
+	 * Finally perform the needed transfer from transfer_from to transfer_to
 	 */
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
 		/*
 		 * Skip changes for same uid or gid or for non-existing quota-type.
 		 */
-		if (transfer_from[cnt] == NODQUOT && transfer_to[cnt] == NODQUOT)
+		if (transfer_from[cnt] == NODQUOT || transfer_to[cnt] == NODQUOT)
 			continue;
 
 		dquot_decr_inodes(transfer_from[cnt], 1);
@@ -1285,40 +1207,34 @@
 		dquot_incr_inodes(transfer_to[cnt], 1);
 		dquot_incr_blocks(transfer_to[cnt], blocks);
 
-		unlock_dquot(transfer_from[cnt]);
-		dqput(transfer_from[cnt]);
-		if (inode->i_dquot[cnt] != NODQUOT) {
-			struct dquot *temp = inode->i_dquot[cnt];
-			inode->i_dquot[cnt] = transfer_to[cnt];
-			unlock_dquot(transfer_to[cnt]);
-			dqput(temp);
-		} else {
-			unlock_dquot(transfer_to[cnt]);
-			dqput(transfer_to[cnt]);
-		}
+		if (inode->i_dquot[cnt] == NODQUOT)
+			BUG();
+		inode->i_dquot[cnt] = transfer_to[cnt];
+		/*
+		 * We've got to release transfer_from[] twice - once for dquot_transfer() and
+		 * once for inode. We don't want to release transfer_to[] as it's now placed in inode
+		 */
+		transfer_to[cnt] = transfer_from[cnt];
 	}
-
-	unlock_kernel();
-	return 0;
-put_all:
-	for (disc = 0; disc < cnt; disc++) {
-		/* There should be none or both pointers set but... */
-		if (transfer_to[disc] != NODQUOT) {
-			unlock_dquot(transfer_to[disc]);
-			dqput(transfer_to[disc]);
-		}
-		if (transfer_from[disc] != NODQUOT) {
-			unlock_dquot(transfer_from[disc]);
-			dqput(transfer_from[disc]);
-		}
+	/* NOBLOCK END. From now on we can block as we wish */
+	ret = QUOTA_OK;
+warn_put_all:
+	flush_warnings(transfer_to, warntype);
+	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+		if (transfer_to[cnt] != NODQUOT)
+			dqput(transfer_to[cnt]);
+		if (transfer_from[cnt] != NODQUOT)
+			dqput(transfer_from[cnt]);
 	}
-	unlock_kernel();
-	return error;
+	return ret;
 }
 
-
 static int __init dquot_init(void)
 {
+	int i;
+
+	for (i = 0; i < NR_DQHASH; i++)
+		INIT_LIST_HEAD(dquot_hash + i);
 	printk(KERN_NOTICE "VFS: Diskquotas version %s initialized\n", __DQUOT_VERSION__);
 	return 0;
 }
@@ -1371,7 +1287,6 @@
 {
 	struct file *filp;
 	short cnt;
-	int enabled = 0;
 	struct quota_mount_options *dqopt = sb_dqopt(sb);
 
 	if (!sb)
@@ -1390,27 +1305,15 @@
 		remove_dquot_ref(sb, cnt);
 		invalidate_dquots(sb, cnt);
 
-		/* Wait for any pending IO - remove me as soon as invalidate is more polite */
-		down(&dqopt->dqio_sem);
 		filp = dqopt->files[cnt];
 		dqopt->files[cnt] = (struct file *)NULL;
 		dqopt->inode_expire[cnt] = 0;
 		dqopt->block_expire[cnt] = 0;
-		up(&dqopt->dqio_sem);
 		fput(filp);
 	}	
-
-	/*
-	 * Check whether any quota is still enabled,
-	 * and if not clear the dq_op pointer.
-	 */
-	for (cnt = 0; cnt < MAXQUOTAS; cnt++)
-		enabled |= is_enabled(dqopt, cnt);
-	if (!enabled)
-		sb->dq_op = NULL;
 	up(&dqopt->dqoff_sem);
 out:
-	return(0);
+	return 0;
 }
 
 static inline int check_quotafile_size(loff_t size)
@@ -1459,8 +1362,8 @@
 	dquot_drop(inode);
 	inode->i_flags |= S_NOQUOTA;
 
-	set_enable_flags(dqopt, type);
 	dqopt->files[type] = f;
+	set_enable_flags(dqopt, type);
 
 	dquot = dqget(sb, 0, type);
 	dqopt->inode_expire[type] = (dquot != NODQUOT) ? dquot->dq_itime : MAX_IQ_TIME;
@@ -1511,11 +1414,11 @@
 		case Q_GETQUOTA:
 			if (((type == USRQUOTA && current->euid != id) ||
 			     (type == GRPQUOTA && !in_egroup_p(id))) &&
-			    !capable(CAP_SYS_RESOURCE))
+			    !capable(CAP_SYS_ADMIN))
 				goto out;
 			break;
 		default:
-			if (!capable(CAP_SYS_RESOURCE))
+			if (!capable(CAP_SYS_ADMIN))
 				goto out;
 	}
 
@@ -1536,13 +1439,16 @@
 		ret = -ENOTBLK;
 		if (!S_ISBLK(mode))
 			goto out;
+		ret = -ENODEV;
 		sb = get_super(dev);
+		if (!sb)
+			goto out;
 	}
 
 	ret = -EINVAL;
 	switch (cmds) {
 		case Q_QUOTAON:
-			ret = sb ? quota_on(sb, type, (char *) addr) : -ENODEV;
+			ret = quota_on(sb, type, (char *) addr);
 			goto out;
 		case Q_QUOTAOFF:
 			ret = quota_off(sb, type);
@@ -1572,9 +1478,7 @@
 			goto out;
 	}
 
-	flags |= QUOTA_SYSCALL;
-
-	ret = -ESRCH;
+	ret = -NODEV;
 	if (sb && sb_has_quota_enabled(sb, type))
 		ret = set_dqblk(sb, id, type, flags, (struct dqblk *) addr);
 out:
diff -urN linux-2.4.11-pre4/fs/ext2/balloc.c linux/fs/ext2/balloc.c
--- linux-2.4.11-pre4/fs/ext2/balloc.c	Wed Jul 11 15:44:45 2001
+++ linux/fs/ext2/balloc.c	Mon Dec  3 23:16:38 2001
@@ -247,7 +247,8 @@
 	return slot;
 }
 
-void ext2_free_blocks (const struct inode * inode, unsigned long block,
+/* Free given blocks, update quota and i_blocks field */
+void ext2_free_blocks (struct inode * inode, unsigned long block,
 		       unsigned long count)
 {
 	struct buffer_head * bh;
@@ -318,7 +319,7 @@
 				      "bit already cleared for block %lu",
 				      block + i);
 		else {
-			DQUOT_FREE_BLOCK(sb, inode, 1);
+			DQUOT_FREE_BLOCK(inode, 1);
 			gdp->bg_free_blocks_count =
 				cpu_to_le16(le16_to_cpu(gdp->bg_free_blocks_count)+1);
 			es->s_free_blocks_count =
@@ -351,8 +352,9 @@
  * is allocated.  Otherwise a forward search is made for a free block; within 
  * each block group the search first looks for an entire free byte in the block
  * bitmap, and then for any free bit if that fails.
+ * This function also updates quota and i_blocks field.
  */
-int ext2_new_block (const struct inode * inode, unsigned long goal,
+int ext2_new_block (struct inode * inode, unsigned long goal,
     u32 * prealloc_count, u32 * prealloc_block, int * err)
 {
 	struct buffer_head * bh;
@@ -508,7 +510,7 @@
 	/*
 	 * Check quota for allocation of this block.
 	 */
-	if(DQUOT_ALLOC_BLOCK(sb, inode, 1)) {
+	if(DQUOT_ALLOC_BLOCK(inode, 1)) {
 		*err = -EDQUOT;
 		goto out;
 	}
@@ -526,7 +528,7 @@
 	if (ext2_set_bit (j, bh->b_data)) {
 		ext2_warning (sb, "ext2_new_block",
 			      "bit already set for block %d", j);
-		DQUOT_FREE_BLOCK(sb, inode, 1);
+		DQUOT_FREE_BLOCK(inode, 1);
 		goto repeat;
 	}
 
@@ -549,13 +551,13 @@
 		for (k = 1;
 		     k < prealloc_goal && (j + k) < EXT2_BLOCKS_PER_GROUP(sb);
 		     k++, next_block++) {
-			if (DQUOT_PREALLOC_BLOCK(sb, inode, 1))
+			if (DQUOT_PREALLOC_BLOCK(inode, 1))
 				break;
 			/* Writer: ->i_prealloc* */
 			if (*prealloc_block + *prealloc_count != next_block ||
 			    ext2_set_bit (j + k, bh->b_data)) {
 				/* Writer: end */
-				DQUOT_FREE_BLOCK(sb, inode, 1);
+				DQUOT_FREE_BLOCK(inode, 1);
  				break;
 			}
 			(*prealloc_count)++;
diff -urN linux-2.4.11-pre4/fs/ext2/ialloc.c linux/fs/ext2/ialloc.c
--- linux-2.4.11-pre4/fs/ext2/ialloc.c	Tue Sep 11 08:49:47 2001
+++ linux/fs/ext2/ialloc.c	Mon Dec  3 23:16:38 2001
@@ -196,7 +196,7 @@
 	 */
 	if (!is_bad_inode(inode)) {
 		/* Quota is already initialized in iput() */
-	    	DQUOT_FREE_INODE(sb, inode);
+	    	DQUOT_FREE_INODE(inode);
 		DQUOT_DROP(inode);
 	}
 
@@ -454,7 +454,7 @@
 	mark_inode_dirty(inode);
 
 	unlock_super (sb);
-	if(DQUOT_ALLOC_INODE(sb, inode)) {
+	if(DQUOT_ALLOC_INODE(inode)) {
 		DQUOT_DROP(inode);
 		inode->i_flags |= S_NOQUOTA;
 		inode->i_nlink = 0;
diff -urN linux-2.4.11-pre4/fs/ext2/inode.c linux/fs/ext2/inode.c
--- linux-2.4.11-pre4/fs/ext2/inode.c	Mon Dec  3 23:16:28 2001
+++ linux/fs/ext2/inode.c	Mon Dec  3 23:16:38 2001
@@ -28,6 +28,7 @@
 #include <linux/smp_lock.h>
 #include <linux/sched.h>
 #include <linux/highuid.h>
+#include <linux/quotaops.h>
 
 static int ext2_update_inode(struct inode * inode, int do_sync);
 
@@ -445,7 +446,7 @@
 
 	/* Verify that place we are splicing to is still there and vacant */
 
-	/* Writer: pointers, ->i_next_alloc*, ->i_blocks */
+	/* Writer: pointers, ->i_next_alloc* */
 	if (!verify_chain(chain, where-1) || *where->p)
 		/* Writer: end */
 		goto changed;
@@ -455,7 +456,6 @@
 	*where->p = where->key;
 	inode->u.ext2_i.i_next_alloc_block = block;
 	inode->u.ext2_i.i_next_alloc_goal = le32_to_cpu(where[num-1].key);
-	inode->i_blocks += num * inode->i_sb->s_blocksize/512;
 
 	/* Writer: end */
 
@@ -702,7 +702,6 @@
  */
 static inline void ext2_free_data(struct inode *inode, u32 *p, u32 *q)
 {
-	int blocks = inode->i_sb->s_blocksize / 512;
 	unsigned long block_to_free = 0, count = 0;
 	unsigned long nr;
 
@@ -716,9 +715,6 @@
 			else if (block_to_free == nr - count)
 				count++;
 			else {
-				/* Writer: ->i_blocks */
-				inode->i_blocks -= blocks * count;
-				/* Writer: end */
 				mark_inode_dirty(inode);
 				ext2_free_blocks (inode, block_to_free, count);
 			free_this:
@@ -728,9 +724,6 @@
 		}
 	}
 	if (count > 0) {
-		/* Writer: ->i_blocks */
-		inode->i_blocks -= blocks * count;
-		/* Writer: end */
 		mark_inode_dirty(inode);
 		ext2_free_blocks (inode, block_to_free, count);
 	}
@@ -775,9 +768,6 @@
 					   (u32*)bh->b_data + addr_per_block,
 					   depth);
 			bforget(bh);
-			/* Writer: ->i_blocks */
-			inode->i_blocks -= inode->i_sb->s_blocksize / 512;
-			/* Writer: end */
 			ext2_free_blocks(inode, nr, 1);
 			mark_inode_dirty(inode);
 		}
@@ -1177,7 +1167,9 @@
 		goto out;
 
 	retval = inode_change_ok(inode, iattr);
-	if (retval != 0)
+	if (retval != 0 || (((iattr->ia_valid & ATTR_UID && iattr->ia_uid != inode->i_uid) ||
+	    (iattr->ia_valid & ATTR_GID && iattr->ia_gid != inode->i_gid)) &&
+	    DQUOT_TRANSFER(inode, iattr)))
 		goto out;
 
 	inode_setattr(inode, iattr);
diff -urN linux-2.4.11-pre4/fs/namespace.c linux/fs/namespace.c
--- linux-2.4.11-pre4/fs/namespace.c	Mon Dec  3 23:16:28 2001
+++ linux/fs/namespace.c	Mon Dec  3 23:16:38 2001
@@ -24,7 +24,8 @@
 #include <linux/nfs_mount.h>
 
 struct vfsmount *do_kern_mount(char *type, int flags, char *name, void *data);
-int do_remount_sb(struct super_block *sb, int flags, char * data);
+int do_remount_sb(struct super_block *sb, int flags, void * data);
+void kill_super(struct super_block *sb);
 
 static struct list_head *mount_hashtable;
 static int hash_mask, hash_bits;
@@ -54,7 +55,6 @@
 		INIT_LIST_HEAD(&mnt->mnt_child);
 		INIT_LIST_HEAD(&mnt->mnt_mounts);
 		INIT_LIST_HEAD(&mnt->mnt_list);
-		mnt->mnt_owner = current->uid;
 	}
 	return mnt;
 }
@@ -66,6 +66,18 @@
 	kmem_cache_free(mnt_cache, mnt);
 }
 
+void set_devname(struct vfsmount *mnt, const char *name)
+{
+	if (name) {
+		int size = strlen(name)+1;
+		char * newname = kmalloc(size, GFP_KERNEL);
+		if (newname) {
+			memcpy(newname, name, size);
+			mnt->mnt_devname = newname;
+		}
+	}
+}
+
 struct vfsmount *lookup_mnt(struct vfsmount *mnt, struct dentry *dentry)
 {
 	struct list_head * head = mount_hashtable + hash(mnt, dentry);
@@ -129,6 +141,30 @@
 	return list_entry(next, struct vfsmount, mnt_child);
 }
 
+static struct vfsmount *
+clone_mnt(struct vfsmount *old, struct dentry *root)
+{
+	struct super_block *sb = old->mnt_sb;
+	struct vfsmount *mnt = alloc_vfsmnt();
+
+	if (mnt) {
+		mnt->mnt_flags = old->mnt_flags;
+		set_devname(mnt, old->mnt_devname);
+		atomic_inc(&sb->s_active);
+		mnt->mnt_sb = sb;
+		mnt->mnt_root = dget(root);
+	}
+	return mnt;
+}
+
+void __mntput(struct vfsmount *mnt)
+{
+	struct super_block *sb = mnt->mnt_sb;
+	dput(mnt->mnt_root);
+	free_vfsmnt(mnt);
+	kill_super(sb);
+}
+
 /* Use octal escapes, like mount does, for embedded spaces etc. */
 static unsigned char need_escaping[] = { ' ', '\t', '\n', '\\' };
 
@@ -189,31 +225,6 @@
 	{ 0, NULL, NULL }
 };
 
-static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root)
-{
-	char *name = old->mnt_devname;
-	struct vfsmount *mnt = alloc_vfsmnt();
-	struct super_block *sb = old->mnt_sb;
-
-	if (!mnt)
-		goto out;
-
-	if (name) {
-		mnt->mnt_devname = kmalloc(strlen(name)+1, GFP_KERNEL);
-		if (mnt->mnt_devname)
-			strcpy(mnt->mnt_devname, name);
-	}
-	mnt->mnt_sb = sb;
-	mnt->mnt_root = dget(root);
-	mnt->mnt_mountpoint = mnt->mnt_root;
-	mnt->mnt_parent = mnt;
-	mnt->mnt_flags = old->mnt_flags;
-
-	atomic_inc(&sb->s_active);
-out:
-	return mnt;
-}
-
 int get_filesystem_info( char *buf )
 {
 	struct list_head *p;
@@ -374,24 +385,22 @@
 	 * somewhat bogus. Suggestions for better replacement?
 	 * Ho-hum... In principle, we might treat that as umount + switch
 	 * to rootfs. GC would eventually take care of the old vfsmount.
-	 * The problem being: we have to implement rootfs and GC for that ;-)
 	 * Actually it makes sense, especially if rootfs would contain a
 	 * /reboot - static binary that would close all descriptors and
 	 * call reboot(9). Then init(8) could umount root and exec /reboot.
 	 */
 	if (mnt == current->fs->rootmnt && !(flags & MNT_DETACH)) {
-		int retval = 0;
 		/*
 		 * Special case for "unmounting" root ...
 		 * we just try to remount it readonly.
 		 */
+		down_write(&sb->s_umount);
 		if (!(sb->s_flags & MS_RDONLY)) {
-			down_write(&sb->s_umount);
 			lock_kernel();
 			retval = do_remount_sb(sb, MS_RDONLY, 0);
 			unlock_kernel();
-			up_write(&sb->s_umount);
 		}
+		up_write(&sb->s_umount);
 		return retval;
 	}
 
@@ -448,12 +457,10 @@
 		goto dput_and_out;
 
 	retval = -EPERM;
-	if (!capable(CAP_SYS_ADMIN) && current->uid!=nd.mnt->mnt_owner)
+	if (!capable(CAP_SYS_ADMIN))
 		goto dput_and_out;
 
 	retval = do_umount(nd.mnt, flags);
-	path_release(&nd);
-	goto out;
 dput_and_out:
 	path_release(&nd);
 out:
@@ -487,9 +494,48 @@
 #endif
 }
 
+static struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry)
+{
+	struct vfsmount *p, *next, *q, *res;
+	struct nameidata nd;
+
+	p = mnt;
+	res = nd.mnt = q = clone_mnt(p, dentry);
+	if (!q)
+		goto Enomem;
+	q->mnt_parent = q;
+	q->mnt_mountpoint = p->mnt_mountpoint;
+
+	while ( (next = next_mnt(p, mnt)) != NULL) {
+		while (p != next->mnt_parent) {
+			p = p->mnt_parent;
+			q = q->mnt_parent;
+		}
+		p = next;
+		nd.mnt = q;
+		nd.dentry = p->mnt_mountpoint;
+		q = clone_mnt(p, p->mnt_root);
+		if (!q)
+			goto Enomem;
+		spin_lock(&dcache_lock);
+		list_add_tail(&q->mnt_list, &res->mnt_list);
+		attach_mnt(q, &nd);
+		spin_unlock(&dcache_lock);
+	}
+	return res;
+Enomem:
+	if (res) {
+		spin_lock(&dcache_lock);
+		umount_tree(res);
+		spin_unlock(&dcache_lock);
+	}
+	return NULL;
+}
+
 /* Will become static */
 int graft_tree(struct vfsmount *mnt, struct nameidata *nd)
 {
+	int err;
 	if (mnt->mnt_sb->s_flags & MS_NOUSER)
 		return -EINVAL;
 
@@ -497,43 +543,38 @@
 	      S_ISDIR(mnt->mnt_root->d_inode->i_mode))
 		return -ENOTDIR;
 
+	err = -ENOENT;
 	down(&nd->dentry->d_inode->i_zombie);
 	if (IS_DEADDIR(nd->dentry->d_inode))
-		goto fail1;
+		goto out_unlock;
 
 	spin_lock(&dcache_lock);
-	if (!IS_ROOT(nd->dentry) && d_unhashed(nd->dentry))
-		goto fail;
-
-	attach_mnt(mnt, nd);
-	list_add(&mnt->mnt_list, vfsmntlist.prev);
-	spin_unlock(&dcache_lock);
-	up(&nd->dentry->d_inode->i_zombie);
-	mntget(mnt);
-	return 0;
-fail:
+	if (IS_ROOT(nd->dentry) || !d_unhashed(nd->dentry)) {
+		struct list_head head;
+		attach_mnt(mnt, nd);
+		list_add_tail(&head, &mnt->mnt_list);
+		list_splice(&head, vfsmntlist.prev);
+		mntget(mnt);
+		err = 0;
+	}
 	spin_unlock(&dcache_lock);
-fail1:
+out_unlock:
 	up(&nd->dentry->d_inode->i_zombie);
-	return -ENOENT;
+	return err;
 }
 
 /*
  * do loopback mount.
  */
-static int do_loopback(struct nameidata *nd, char *old_name)
+static int do_loopback(struct nameidata *nd, char *old_name, int recurse)
 {
 	struct nameidata old_nd;
 	struct vfsmount *mnt = NULL;
-	int err;
-
-	err = mount_is_safe(nd);
+	int err = mount_is_safe(nd);
 	if (err)
 		return err;
-
 	if (!old_name || !*old_name)
 		return -EINVAL;
-
 	if (path_init(old_name, LOOKUP_POSITIVE|LOOKUP_FOLLOW, &old_nd))
 		err = path_walk(old_name, &old_nd);
 	if (err)
@@ -541,12 +582,18 @@
 
 	down(&mount_sem);
 	err = -EINVAL;
-	if (check_mnt(nd->mnt)) {
+	if (check_mnt(nd->mnt) && (!recurse || check_mnt(old_nd.mnt))) {
 		err = -ENOMEM;
-		mnt = clone_mnt(old_nd.mnt, old_nd.dentry);
+		if (recurse)
+			mnt = copy_tree(old_nd.mnt, old_nd.dentry);
+		else
+			mnt = clone_mnt(old_nd.mnt, old_nd.dentry);
 	}
+
 	if (mnt) {
 		err = graft_tree(mnt, nd);
+		if (err && recurse)
+			umount_tree(mnt);
 		mntput(mnt);
 	}
 
@@ -557,10 +604,11 @@
 
 /*
  * change filesystem flags. dir should be a physical root of filesystem.
+ * If you've mounted a non-root directory somewhere and want to do remount
  * on it - tough luck.
  */
 
-static int do_remount(struct nameidata *nd,int flags,int mnt_flags,char *data)
+static int do_remount(struct nameidata *nd,int flags,int mnt_flags,void *data)
 {
 	int err;
 	struct super_block * sb = nd->mnt->mnt_sb;
@@ -694,10 +742,10 @@
 		return retval;
 
 	if (flags & MS_REMOUNT)
-		retval = do_remount(&nd, flags&~MS_REMOUNT, mnt_flags,
-				  (char *)data_page);
+		retval = do_remount(&nd, flags & ~MS_REMOUNT, mnt_flags,
+				    data_page);
 	else if (flags & MS_BIND)
-		retval = do_loopback(&nd, dev_name);
+		retval = do_loopback(&nd, dev_name, flags & MS_REC);
 	else
 		retval = do_add_mount(&nd, type_page, flags, mnt_flags,
 				      dev_name, data_page);
diff -urN linux-2.4.11-pre4/fs/nfsd/vfs.c linux/fs/nfsd/vfs.c
--- linux-2.4.11-pre4/fs/nfsd/vfs.c	Mon Dec  3 23:16:28 2001
+++ linux/fs/nfsd/vfs.c	Mon Dec  3 23:16:38 2001
@@ -296,16 +296,6 @@
 
 
 	iap->ia_valid |= ATTR_CTIME;
-#ifdef CONFIG_QUOTA
-	/* DQUOT_TRANSFER needs both ia_uid and ia_gid defined */
-	if (iap->ia_valid & (ATTR_UID|ATTR_GID)) {
-		if (! (iap->ia_valid & ATTR_UID))
-			iap->ia_uid = inode->i_uid;
-		if (! (iap->ia_valid & ATTR_GID))
-			iap->ia_gid = inode->i_gid;
-		iap->ia_valid |= ATTR_UID|ATTR_GID;
-	}
-#endif /* CONFIG_QUOTA */
 
 	if (iap->ia_valid & ATTR_SIZE) {
 		fh_lock(fhp);
@@ -313,12 +303,7 @@
 	}
 	err = nfserr_notsync;
 	if (!check_guard || guardtime == inode->i_ctime) {
-#ifdef CONFIG_QUOTA
-		if (iap->ia_valid & (ATTR_UID|ATTR_GID)) 
-			err = DQUOT_TRANSFER(dentry, iap);
-		else
-#endif
-			err = notify_change(dentry, iap);
+		err = notify_change(dentry, iap);
 		err = nfserrno(err);
 	}
 	if (size_change) {
diff -urN linux-2.4.11-pre4/fs/open.c linux/fs/open.c
--- linux-2.4.11-pre4/fs/open.c	Mon Sep 17 13:16:30 2001
+++ linux/fs/open.c	Mon Dec  3 23:16:38 2001
@@ -554,7 +554,7 @@
 		newattrs.ia_mode &= ~S_ISGID;
 		newattrs.ia_valid |= ATTR_MODE;
 	}
-	error = DQUOT_TRANSFER(dentry, &newattrs);
+	error = notify_change(dentry, &newattrs);
 out:
 	return error;
 }
diff -urN linux-2.4.11-pre4/fs/super.c linux/fs/super.c
--- linux-2.4.11-pre4/fs/super.c	Mon Dec  3 23:16:28 2001
+++ linux/fs/super.c	Mon Dec  3 23:16:38 2001
@@ -46,7 +46,7 @@
 
 extern int root_mountflags;
 
-int do_remount_sb(struct super_block *sb, int flags, char * data);
+int do_remount_sb(struct super_block *sb, int flags, void * data);
 
 /* this is initialized in init/main.c */
 kdev_t ROOT_DEV;
@@ -273,23 +273,12 @@
 
 struct vfsmount *alloc_vfsmnt(void);
 void free_vfsmnt(struct vfsmount *mnt);
-void umount_tree(struct vfsmount *mnt);
+void set_devname(struct vfsmount *mnt, const char *name);
 
 /* Will go away */
 extern struct vfsmount *root_vfsmnt;
 extern int graft_tree(struct vfsmount *mnt, struct nameidata *nd);
 
-static void kill_super(struct super_block *);
-
-void __mntput(struct vfsmount *mnt)
-{
-	struct super_block *sb = mnt->mnt_sb;
-
-	dput(mnt->mnt_root);
-	free_vfsmnt(mnt);
-	kill_super(sb);
-}
-
 static inline void __put_super(struct super_block *sb)
 {
 	spin_lock(&sb_lock);
@@ -404,9 +393,7 @@
         struct statfs sbuf;
 	int err = -EINVAL;
 
-	lock_kernel();
         s = get_super(to_kdev_t(dev));
-	unlock_kernel();
         if (s == NULL)
                 goto out;
 	err = vfs_statfs(s, &sbuf);
@@ -727,7 +714,7 @@
 	}
 }
 
-static void kill_super(struct super_block *sb)
+void kill_super(struct super_block *sb)
 {
 	struct block_device *bdev;
 	kdev_t dev;
@@ -789,7 +776,7 @@
  * is used as a reference - file system type and the device are ignored.
  */
 
-int do_remount_sb(struct super_block *sb, int flags, char *data)
+int do_remount_sb(struct super_block *sb, int flags, void *data)
 {
 	int retval;
 	
@@ -846,12 +833,7 @@
 		mnt = ERR_PTR(-ENOMEM);
 		goto fs_out;
 	}
-	if (name) {
-		mnt->mnt_devname = kmalloc(strlen(name)+1, GFP_KERNEL);
-		if (mnt->mnt_devname)
-			strcpy(mnt->mnt_devname, name);
-	}
-
+	set_devname(mnt, name);
 	/* get locked superblock */
 	if (fstype->fs_flags & FS_REQUIRES_DEV)
 		sb = get_sb_bdev(fstype, name, flags, data);
@@ -1042,9 +1024,7 @@
 	if (!vfsmnt)
 		panic("VFS: alloc_vfsmnt failed for root fs");
 
-	vfsmnt->mnt_devname = kmalloc(strlen(name)+1, GFP_KERNEL);
-	if (vfsmnt->mnt_devname)
-		strcpy(vfsmnt->mnt_devname, name);
+	set_devname(vfsmnt, name);
 	vfsmnt->mnt_sb = sb;
 	vfsmnt->mnt_root = dget(sb->s_root);
 
diff -urN linux-2.4.11-pre4/fs/udf/balloc.c linux/fs/udf/balloc.c
--- linux-2.4.11-pre4/fs/udf/balloc.c	Mon Jun 11 19:15:27 2001
+++ linux/fs/udf/balloc.c	Mon Dec  3 23:16:38 2001
@@ -142,7 +142,7 @@
 	return slot;
 }
 
-static void udf_bitmap_free_blocks(const struct inode * inode,
+static void udf_bitmap_free_blocks(struct inode * inode,
 	struct udf_bitmap *bitmap, lb_addr bloc, Uint32 offset, Uint32 count)
 {
 	struct buffer_head * bh = NULL;
@@ -200,7 +200,7 @@
 		}
 		else
 		{
-			DQUOT_FREE_BLOCK(sb, inode, 1);
+			DQUOT_FREE_BLOCK(inode, 1);
 			if (UDF_SB_LVIDBH(sb))
 			{
 				UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)] =
@@ -223,7 +223,7 @@
 	return;
 }
 
-static int udf_bitmap_prealloc_blocks(const struct inode * inode,
+static int udf_bitmap_prealloc_blocks(struct inode * inode,
 	struct udf_bitmap *bitmap, Uint16 partition, Uint32 first_block,
 	Uint32 block_count)
 {
@@ -265,12 +265,12 @@
 	{
 		if (!udf_test_bit(bit, bh->b_data))
 			goto out;
-		else if (DQUOT_PREALLOC_BLOCK(sb, inode, 1))
+		else if (DQUOT_PREALLOC_BLOCK(inode, 1))
 			goto out;
 		else if (!udf_clear_bit(bit, bh->b_data))
 		{
 			udf_debug("bit already cleared for block %d\n", bit);
-			DQUOT_FREE_BLOCK(sb, inode, 1);
+			DQUOT_FREE_BLOCK(inode, 1);
 			goto out;
 		}
 		block_count --;
@@ -293,7 +293,7 @@
 	return alloc_count;
 }
 
-static int udf_bitmap_new_block(const struct inode * inode,
+static int udf_bitmap_new_block(struct inode * inode,
 	struct udf_bitmap *bitmap, Uint16 partition, Uint32 goal, int *err)
 {
 	int newbit, bit=0, block, block_group, group_start;
@@ -404,7 +404,7 @@
 	/*
 	 * Check quota for allocation of this block.
 	 */
-	if (DQUOT_ALLOC_BLOCK(sb, inode, 1))
+	if (DQUOT_ALLOC_BLOCK(inode, 1))
 	{
 		unlock_super(sb);
 		*err = -EDQUOT;
@@ -439,7 +439,7 @@
 	return 0;
 }
 
-static void udf_table_free_blocks(const struct inode * inode,
+static void udf_table_free_blocks(struct inode * inode,
 	struct inode * table, lb_addr bloc, Uint32 offset, Uint32 count)
 {
 	struct super_block * sb;
@@ -475,7 +475,7 @@
 
 	/* We do this up front - There are some error conditions that could occure,
 	   but.. oh well */
-	DQUOT_FREE_BLOCK(sb, inode, count);
+	DQUOT_FREE_BLOCK(inode, count);
 	if (UDF_SB_LVIDBH(sb))
 	{
 		UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)] =
@@ -690,7 +690,7 @@
 	return;
 }
 
-static int udf_table_prealloc_blocks(const struct inode * inode,
+static int udf_table_prealloc_blocks(struct inode * inode,
 	struct inode *table, Uint16 partition, Uint32 first_block,
 	Uint32 block_count)
 {
@@ -887,7 +887,7 @@
 	return newblock;
 }
 
-inline void udf_free_blocks(const struct inode * inode, lb_addr bloc,
+inline void udf_free_blocks(struct inode * inode, lb_addr bloc,
 	Uint32 offset, Uint32 count)
 {
 	if (UDF_SB_PARTFLAGS(inode->i_sb, bloc.partitionReferenceNum) & UDF_PART_FLAG_UNALLOC_BITMAP)
@@ -918,7 +918,7 @@
 		return;
 }
 
-inline int udf_prealloc_blocks(const struct inode * inode, Uint16 partition,
+inline int udf_prealloc_blocks(struct inode * inode, Uint16 partition,
 	Uint32 first_block, Uint32 block_count)
 {
 	if (UDF_SB_PARTFLAGS(inode->i_sb, partition) & UDF_PART_FLAG_UNALLOC_BITMAP)
@@ -949,7 +949,7 @@
 		return 0;
 }
 
-inline int udf_new_block(const struct inode * inode, Uint16 partition,
+inline int udf_new_block(struct inode * inode, Uint16 partition,
 	Uint32 goal, int *err)
 {
 	if (UDF_SB_PARTFLAGS(inode->i_sb, partition) & UDF_PART_FLAG_UNALLOC_BITMAP)
diff -urN linux-2.4.11-pre4/fs/udf/ialloc.c linux/fs/udf/ialloc.c
--- linux-2.4.11-pre4/fs/udf/ialloc.c	Mon Jul  2 14:10:02 2001
+++ linux/fs/udf/ialloc.c	Mon Dec  3 23:16:38 2001
@@ -44,7 +44,7 @@
 	 * Note: we must free any quota before locking the superblock,
 	 * as writing the quota to disk may need the lock as well.
 	 */
-	DQUOT_FREE_INODE(sb, inode);
+	DQUOT_FREE_INODE(inode);
 	DQUOT_DROP(inode);
 
 	lock_super(sb);
@@ -70,7 +70,7 @@
 	udf_free_blocks(inode, UDF_I_LOCATION(inode), 0, 1);
 }
 
-struct inode * udf_new_inode (const struct inode *dir, int mode, int * err)
+struct inode * udf_new_inode (struct inode *dir, int mode, int * err)
 {
 	struct super_block *sb;
 	struct inode * inode;
@@ -153,9 +153,10 @@
 	mark_inode_dirty(inode);
 
 	unlock_super(sb);
-	if (DQUOT_ALLOC_INODE(sb, inode))
+	if (DQUOT_ALLOC_INODE(inode))
 	{
-		sb->dq_op->drop(inode);
+		DQUOT_DROP(inode);
+		inode->i_flags |= S_NOQUOTA;
 		inode->i_nlink = 0;
 		iput(inode);
 		*err = -EDQUOT;
diff -urN linux-2.4.11-pre4/fs/udf/udfdecl.h linux/fs/udf/udfdecl.h
--- linux-2.4.11-pre4/fs/udf/udfdecl.h	Mon Jun 11 19:15:27 2001
+++ linux/fs/udf/udfdecl.h	Mon Dec  3 23:16:38 2001
@@ -165,15 +165,15 @@
 
 /* ialloc.c */
 extern void udf_free_inode(struct inode *);
-extern struct inode * udf_new_inode (const struct inode *, int, int *);
+extern struct inode * udf_new_inode (struct inode *, int, int *);
 
 /* truncate.c */
 extern void udf_truncate_extents(struct inode *);
 
 /* balloc.c */
-extern void udf_free_blocks(const struct inode *, lb_addr, Uint32, Uint32);
-extern int udf_prealloc_blocks(const struct inode *, Uint16, Uint32, Uint32);
-extern int udf_new_block(const struct inode *, Uint16, Uint32, int *);
+extern void udf_free_blocks(struct inode *, lb_addr, Uint32, Uint32);
+extern int udf_prealloc_blocks(struct inode *, Uint16, Uint32, Uint32);
+extern int udf_new_block(struct inode *, Uint16, Uint32, int *);
 
 /* fsync.c */
 extern int udf_fsync_file(struct file *, struct dentry *, int);
diff -urN linux-2.4.11-pre4/fs/ufs/balloc.c linux/fs/ufs/balloc.c
--- linux-2.4.11-pre4/fs/ufs/balloc.c	Thu Sep 13 16:04:43 2001
+++ linux/fs/ufs/balloc.c	Mon Dec  3 23:16:38 2001
@@ -85,7 +85,7 @@
 			"bit already cleared for fragment %u", i);
 	}
 	
-	DQUOT_FREE_BLOCK (sb, inode, count);
+	DQUOT_FREE_BLOCK (inode, count);
 	ADD_SWAB32(ucg->cg_cs.cs_nffree, count);
 	ADD_SWAB32(usb1->fs_cstotal.cs_nffree, count);
 	ADD_SWAB32(sb->fs_cs(cgno).cs_nffree, count);
@@ -187,7 +187,7 @@
 		ubh_setblock(UCPI_UBH, ucpi->c_freeoff, blkno);
 		if ((sb->u.ufs_sb.s_flags & UFS_CG_MASK) == UFS_CG_44BSD)
 			ufs_clusteracct (sb, ucpi, blkno, 1);
-		DQUOT_FREE_BLOCK(sb, inode, uspi->s_fpb);
+		DQUOT_FREE_BLOCK(inode, uspi->s_fpb);
 		INC_SWAB32(ucg->cg_cs.cs_nbfree);
 		INC_SWAB32(usb1->fs_cstotal.cs_nbfree);
 		INC_SWAB32(sb->fs_cs(cgno).cs_nbfree);
@@ -451,7 +451,7 @@
 		INC_SWAB32(ucg->cg_frsum[fragsize - count]);
 	for (i = oldcount; i < newcount; i++)
 		ubh_clrbit (UCPI_UBH, ucpi->c_freeoff, fragno + i);
-	if(DQUOT_ALLOC_BLOCK(sb, inode, count)) {
+	if(DQUOT_ALLOC_BLOCK(inode, count)) {
 		*err = -EDQUOT;
 		return 0;
 	}
@@ -558,7 +558,7 @@
 		for (i = count; i < uspi->s_fpb; i++)
 			ubh_setbit (UCPI_UBH, ucpi->c_freeoff, goal + i);
 		i = uspi->s_fpb - count;
-		DQUOT_FREE_BLOCK(sb, inode, i);
+		DQUOT_FREE_BLOCK(inode, i);
 		ADD_SWAB32(ucg->cg_cs.cs_nffree, i);
 		ADD_SWAB32(usb1->fs_cstotal.cs_nffree, i);
 		ADD_SWAB32(sb->fs_cs(cgno).cs_nffree, i);
@@ -569,7 +569,7 @@
 	result = ufs_bitmap_search (sb, ucpi, goal, allocsize);
 	if (result == (unsigned)-1)
 		return 0;
-	if(DQUOT_ALLOC_BLOCK(sb, inode, count)) {
+	if(DQUOT_ALLOC_BLOCK(inode, count)) {
 		*err = -EDQUOT;
 		return 0;
 	}
@@ -639,7 +639,7 @@
 	ubh_clrblock (UCPI_UBH, ucpi->c_freeoff, blkno);
 	if ((sb->u.ufs_sb.s_flags & UFS_CG_MASK) == UFS_CG_44BSD)
 		ufs_clusteracct (sb, ucpi, blkno, -1);
-	if(DQUOT_ALLOC_BLOCK(sb, inode, uspi->s_fpb)) {
+	if(DQUOT_ALLOC_BLOCK(inode, uspi->s_fpb)) {
 		*err = -EDQUOT;
 		return (unsigned)-1;
 	}
diff -urN linux-2.4.11-pre4/fs/ufs/ialloc.c linux/fs/ufs/ialloc.c
--- linux-2.4.11-pre4/fs/ufs/ialloc.c	Thu Nov 16 13:18:26 2000
+++ linux/fs/ufs/ialloc.c	Mon Dec  3 23:16:38 2001
@@ -100,7 +100,7 @@
 
 	is_directory = S_ISDIR(inode->i_mode);
 
-	DQUOT_FREE_INODE(sb, inode);
+	DQUOT_FREE_INODE(inode);
 	DQUOT_DROP(inode);
 
 	clear_inode (inode);
@@ -278,8 +278,9 @@
 
 	unlock_super (sb);
 
-	if(DQUOT_ALLOC_INODE(sb, inode)) {
-		sb->dq_op->drop(inode);
+	if (DQUOT_ALLOC_INODE(inode)) {
+		DQUOT_DROP(inode);
+		inode->i_flags |= S_NOQUOTA;
 		inode->i_nlink = 0;
 		iput(inode);
 		*err = -EDQUOT;
@@ -293,6 +294,7 @@
 
 failed:
 	unlock_super (sb);
+	make_bad_inode(inode);
 	iput (inode);
 	UFSD(("EXIT (FAILED)\n"))
 	return NULL;
diff -urN linux-2.4.11-pre4/include/asm-alpha/compiler.h linux/include/asm-alpha/compiler.h
--- linux-2.4.11-pre4/include/asm-alpha/compiler.h	Sun May 20 12:11:39 2001
+++ linux/include/asm-alpha/compiler.h	Mon Dec  3 23:16:38 2001
@@ -72,13 +72,4 @@
   __asm__("stw %1,%0" : "=m"(mem) : "r"(val))
 #endif
 
-/* Somewhere in the middle of the GCC 2.96 development cycle, we implemented
-   a mechanism by which the user can annotate likely branch directions and
-   expect the blocks to be reordered appropriately.  Define __builtin_expect
-   to nothing for earlier compilers.  */
-
-#if __GNUC__ == 2 && __GNUC_MINOR__ < 96
-#define __builtin_expect(x, expected_value) (x)
-#endif
-
 #endif /* __ALPHA_COMPILER_H */
diff -urN linux-2.4.11-pre4/include/asm-alpha/page.h linux/include/asm-alpha/page.h
--- linux-2.4.11-pre4/include/asm-alpha/page.h	Thu May 24 15:20:18 2001
+++ linux/include/asm-alpha/page.h	Mon Dec  3 23:16:38 2001
@@ -80,7 +80,7 @@
 	return order;
 }
 
-#endif /* !ASSEMBLY */
+#endif /* !__ASSEMBLY__ */
 
 /* to align the pointer to the (next) page boundary */
 #define PAGE_ALIGN(addr)	(((addr)+PAGE_SIZE-1)&PAGE_MASK)
diff -urN linux-2.4.11-pre4/include/asm-alpha/processor.h linux/include/asm-alpha/processor.h
--- linux-2.4.11-pre4/include/asm-alpha/processor.h	Mon Sep 17 15:30:14 2001
+++ linux/include/asm-alpha/processor.h	Mon Dec  3 23:16:38 2001
@@ -148,6 +148,8 @@
 #define init_task	(init_task_union.task)
 #define init_stack	(init_task_union.stack)
 
+#define cpu_relax()	do { } while (0)
+
 #define ARCH_HAS_PREFETCH
 #define ARCH_HAS_PREFETCHW
 #define ARCH_HAS_SPINLOCK_PREFETCH
diff -urN linux-2.4.11-pre4/include/asm-alpha/rwsem.h linux/include/asm-alpha/rwsem.h
--- linux-2.4.11-pre4/include/asm-alpha/rwsem.h	Tue Jul 10 20:08:51 2001
+++ linux/include/asm-alpha/rwsem.h	Mon Dec  3 23:16:38 2001
@@ -12,7 +12,7 @@
 
 #ifdef __KERNEL__
 
-#include <asm/compiler.h>
+#include <linux/compiler.h>
 #include <linux/list.h>
 #include <linux/spinlock.h>
 
diff -urN linux-2.4.11-pre4/include/asm-alpha/semaphore.h linux/include/asm-alpha/semaphore.h
--- linux-2.4.11-pre4/include/asm-alpha/semaphore.h	Sun May 20 12:11:39 2001
+++ linux/include/asm-alpha/semaphore.h	Mon Dec  3 23:16:38 2001
@@ -11,7 +11,7 @@
 #include <asm/current.h>
 #include <asm/system.h>
 #include <asm/atomic.h>
-#include <asm/compiler.h>
+#include <linux/compiler.h>
 #include <linux/wait.h>
 #include <linux/rwsem.h>
 
diff -urN linux-2.4.11-pre4/include/asm-arm/processor.h linux/include/asm-arm/processor.h
--- linux-2.4.11-pre4/include/asm-arm/processor.h	Mon Sep 17 15:30:14 2001
+++ linux/include/asm-arm/processor.h	Mon Dec  3 23:16:38 2001
@@ -112,6 +112,8 @@
 #define init_task	(init_task_union.task)
 #define init_stack	(init_task_union.stack)
 
+#define cpu_relax()	do { } while (0)
+
 /*
  * Create a new kernel thread
  */
diff -urN linux-2.4.11-pre4/include/asm-cris/processor.h linux/include/asm-cris/processor.h
--- linux-2.4.11-pre4/include/asm-cris/processor.h	Mon Sep 17 15:30:14 2001
+++ linux/include/asm-cris/processor.h	Mon Dec  3 23:16:38 2001
@@ -141,4 +141,6 @@
 #define init_task       (init_task_union.task)
 #define init_stack      (init_task_union.stack)
 
+#define cpu_relax()	do { } while (0)
+
 #endif /* __ASM_CRIS_PROCESSOR_H */
diff -urN linux-2.4.11-pre4/include/asm-i386/processor.h linux/include/asm-i386/processor.h
--- linux-2.4.11-pre4/include/asm-i386/processor.h	Sun Sep 23 10:31:02 2001
+++ linux/include/asm-i386/processor.h	Mon Dec  3 23:16:38 2001
@@ -476,6 +476,8 @@
 	__asm__ __volatile__("rep;nop");
 }
 
+#define cpu_relax()	rep_nop()
+
 /* Prefetch instructions for Pentium III and AMD Athlon */
 #ifdef 	CONFIG_MPENTIUMIII
 
diff -urN linux-2.4.11-pre4/include/asm-i386/smp.h linux/include/asm-i386/smp.h
--- linux-2.4.11-pre4/include/asm-i386/smp.h	Mon Dec  3 23:16:28 2001
+++ linux/include/asm-i386/smp.h	Mon Dec  3 23:16:38 2001
@@ -4,14 +4,14 @@
 /*
  * We need the APIC definitions automatically as part of 'smp.h'
  */
-#ifndef ASSEMBLY
+#ifndef __ASSEMBLY__
 #include <linux/config.h>
 #include <linux/threads.h>
 #include <linux/ptrace.h>
 #endif
 
 #ifdef CONFIG_X86_LOCAL_APIC
-#ifndef ASSEMBLY
+#ifndef __ASSEMBLY__
 #include <asm/fixmap.h>
 #include <asm/bitops.h>
 #include <asm/mpspec.h>
@@ -22,7 +22,7 @@
 #endif
 #endif
 
-#if CONFIG_SMP
+#ifdef CONFIG_SMP
 # ifdef CONFIG_MULTIQUAD
 #  define TARGET_CPUS 0xf     /* all CPUs in *THIS* quad */
 #  define INT_DELIVERY_MODE 0     /* physical delivery on LOCAL quad */
@@ -31,11 +31,22 @@
 #  define INT_DELIVERY_MODE 1     /* logical delivery broadcast to all procs */
 # endif
 #else
+# define INT_DELIVERY_MODE 0     /* physical delivery on LOCAL quad */
 # define TARGET_CPUS 0x01
 #endif
 
+#ifndef clustered_apic_mode
+ #ifdef CONFIG_MULTIQUAD
+  #define clustered_apic_mode (1)
+  #define esr_disable (1)
+ #else /* !CONFIG_MULTIQUAD */
+  #define clustered_apic_mode (0)
+  #define esr_disable (0)
+ #endif /* CONFIG_MULTIQUAD */
+#endif 
+
 #ifdef CONFIG_SMP
-#ifndef ASSEMBLY
+#ifndef __ASSEMBLY__
 
 /*
  * Private routines/data
@@ -77,16 +88,6 @@
 extern volatile int cpu_to_logical_apicid[NR_CPUS];
 extern volatile int logical_apicid_to_cpu[MAX_APICID];
 
-#ifndef clustered_apic_mode
- #ifdef CONFIG_MULTIQUAD
-  #define clustered_apic_mode (1)
-  #define esr_disable (1)
- #else /* !CONFIG_MULTIQUAD */
-  #define clustered_apic_mode (0)
-  #define esr_disable (0)
- #endif /* CONFIG_MULTIQUAD */
-#endif 
-
 /*
  * General functions that each host system must provide.
  */
@@ -114,7 +115,7 @@
 	return GET_APIC_LOGICAL_ID(*(unsigned long *)(APIC_BASE+APIC_LDR));
 }
 
-#endif /* !ASSEMBLY */
+#endif /* !__ASSEMBLY__ */
 
 #define NO_PROC_ID		0xFF		/* No processor magic marker */
 
diff -urN linux-2.4.11-pre4/include/asm-ia64/processor.h linux/include/asm-ia64/processor.h
--- linux-2.4.11-pre4/include/asm-ia64/processor.h	Mon Sep 17 15:30:14 2001
+++ linux/include/asm-ia64/processor.h	Mon Dec  3 23:16:38 2001
@@ -969,6 +969,8 @@
 	return result;
 }
 
+#define cpu_relax()	do { } while (0)
+
 
 #define ARCH_HAS_PREFETCH
 #define ARCH_HAS_PREFETCHW
diff -urN linux-2.4.11-pre4/include/asm-m68k/processor.h linux/include/asm-m68k/processor.h
--- linux-2.4.11-pre4/include/asm-m68k/processor.h	Mon Sep 17 15:30:14 2001
+++ linux/include/asm-m68k/processor.h	Mon Dec  3 23:16:38 2001
@@ -155,4 +155,6 @@
 #define init_task	(init_task_union.task)
 #define init_stack	(init_task_union.stack)
 
+#define cpu_relax()	do { } while (0)
+
 #endif
diff -urN linux-2.4.11-pre4/include/asm-mips/processor.h linux/include/asm-mips/processor.h
--- linux-2.4.11-pre4/include/asm-mips/processor.h	Mon Sep 17 15:30:14 2001
+++ linux/include/asm-mips/processor.h	Mon Dec  3 23:16:38 2001
@@ -259,6 +259,8 @@
 #define init_task	(init_task_union.task)
 #define init_stack	(init_task_union.stack)
 
+#define cpu_relax()	do { } while (0)
+
 #endif /* !defined (_LANGUAGE_ASSEMBLY) */
 #endif /* __KERNEL__ */
 
diff -urN linux-2.4.11-pre4/include/asm-mips64/processor.h linux/include/asm-mips64/processor.h
--- linux-2.4.11-pre4/include/asm-mips64/processor.h	Mon Sep 17 15:30:14 2001
+++ linux/include/asm-mips64/processor.h	Mon Dec  3 23:16:38 2001
@@ -290,6 +290,8 @@
 #define init_task	(init_task_union.task)
 #define init_stack	(init_task_union.stack)
 
+#define cpu_relax()	do { } while (0)
+
 #endif /* !defined (_LANGUAGE_ASSEMBLY) */
 #endif /* __KERNEL__ */
 
diff -urN linux-2.4.11-pre4/include/asm-parisc/processor.h linux/include/asm-parisc/processor.h
--- linux-2.4.11-pre4/include/asm-parisc/processor.h	Mon Sep 17 15:30:14 2001
+++ linux/include/asm-parisc/processor.h	Mon Dec  3 23:16:38 2001
@@ -333,5 +333,7 @@
 #define init_task (init_task_union.task) 
 #define init_stack (init_task_union.stack)
 
+#define cpu_relax()	do { } while (0)
+
 
 #endif /* __ASM_PARISC_PROCESSOR_H */
diff -urN linux-2.4.11-pre4/include/asm-ppc/processor.h linux/include/asm-ppc/processor.h
--- linux-2.4.11-pre4/include/asm-ppc/processor.h	Mon Sep 17 15:30:14 2001
+++ linux/include/asm-ppc/processor.h	Mon Dec  3 23:16:38 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.processor.h 1.28 08/17/01 15:23:17 paulus
+ * BK Id: SCCS/s.processor.h 1.31 10/05/01 16:26:22 paulus
  */
 #ifdef __KERNEL__
 #ifndef __ASM_PPC_PROCESSOR_H
@@ -654,6 +654,27 @@
 
 /* In misc.c */
 void _nmask_and_or_msr(unsigned long nmask, unsigned long or_val);
+
+#define cpu_relax()	do { } while (0)
+
+/*
+ * Prefetch macros.
+ */
+#define ARCH_HAS_PREFETCH
+#define ARCH_HAS_PREFETCHW
+#define ARCH_HAS_SPINLOCK_PREFETCH
+
+extern inline void prefetch(const void *x)
+{
+	 __asm__ __volatile__ ("dcbt 0,%0" : : "r" (x));
+}
+
+extern inline void prefetchw(const void *x)
+{
+	 __asm__ __volatile__ ("dcbtst 0,%0" : : "r" (x));
+}
+
+#define spin_lock_prefetch(x)	prefetchw(x)
 
 #endif /* !__ASSEMBLY__ */
 
diff -urN linux-2.4.11-pre4/include/asm-s390/processor.h linux/include/asm-s390/processor.h
--- linux-2.4.11-pre4/include/asm-s390/processor.h	Mon Sep 17 15:30:14 2001
+++ linux/include/asm-s390/processor.h	Mon Dec  3 23:16:38 2001
@@ -149,6 +149,8 @@
 #define init_task       (init_task_union.task)
 #define init_stack      (init_task_union.stack)
 
+#define cpu_relax()	do { } while (0)
+
 /*
  * Set of msr bits that gdb can change on behalf of a process.
  */
diff -urN linux-2.4.11-pre4/include/asm-s390x/processor.h linux/include/asm-s390x/processor.h
--- linux-2.4.11-pre4/include/asm-s390x/processor.h	Mon Sep 17 15:30:15 2001
+++ linux/include/asm-s390x/processor.h	Mon Dec  3 23:16:38 2001
@@ -159,6 +159,8 @@
 #define init_task       (init_task_union.task)
 #define init_stack      (init_task_union.stack)
 
+#define cpu_relax()	do { } while (0)
+
 /*
  * Set of msr bits that gdb can change on behalf of a process.
  */
diff -urN linux-2.4.11-pre4/include/asm-sh/processor.h linux/include/asm-sh/processor.h
--- linux-2.4.11-pre4/include/asm-sh/processor.h	Mon Sep 17 15:30:15 2001
+++ linux/include/asm-sh/processor.h	Mon Dec  3 23:16:38 2001
@@ -218,4 +218,6 @@
 #define init_task	(init_task_union.task)
 #define init_stack	(init_task_union.stack)
 
+#define cpu_relax()	do { } while (0)
+
 #endif /* __ASM_SH_PROCESSOR_H */
diff -urN linux-2.4.11-pre4/include/asm-sparc/processor.h linux/include/asm-sparc/processor.h
--- linux-2.4.11-pre4/include/asm-sparc/processor.h	Thu Sep 20 14:11:58 2001
+++ linux/include/asm-sparc/processor.h	Mon Dec  3 23:16:38 2001
@@ -201,6 +201,8 @@
 #define init_task	(init_task_union.task)
 #define init_stack	(init_task_union.stack)
 
+#define cpu_relax()	do { } while (0)
+
 #endif
 
 #endif /* __ASM_SPARC_PROCESSOR_H */
diff -urN linux-2.4.11-pre4/include/asm-sparc64/processor.h linux/include/asm-sparc64/processor.h
--- linux-2.4.11-pre4/include/asm-sparc64/processor.h	Thu Sep 20 14:11:58 2001
+++ linux/include/asm-sparc64/processor.h	Mon Dec  3 23:16:38 2001
@@ -276,6 +276,8 @@
 #define init_task	(init_task_union.task)
 #define init_stack	(init_task_union.stack)
 
+#define cpu_relax()	do { } while (0)
+
 #endif /* __KERNEL__ */
 
 #endif /* !(__ASSEMBLY__) */
diff -urN linux-2.4.11-pre4/include/linux/agp_backend.h linux/include/linux/agp_backend.h
--- linux-2.4.11-pre4/include/linux/agp_backend.h	Sun Sep 23 09:52:38 2001
+++ linux/include/linux/agp_backend.h	Mon Dec  3 23:16:38 2001
@@ -46,6 +46,7 @@
 	INTEL_GX,
 	INTEL_I810,
 	INTEL_I815,
+	INTEL_I830_M,
 	INTEL_I840,
 	INTEL_I850,
 	VIA_GENERIC,
diff -urN linux-2.4.11-pre4/include/linux/dcache.h linux/include/linux/dcache.h
--- linux-2.4.11-pre4/include/linux/dcache.h	Sun Sep 23 10:31:01 2001
+++ linux/include/linux/dcache.h	Mon Dec  3 23:16:38 2001
@@ -178,6 +178,9 @@
 extern int shrink_icache_memory(int, int);
 extern void prune_icache(int);
 
+/* quota cache memory management (defined in linux/fs/dquot.c) */
+extern int shrink_dqcache_memory(int, unsigned int);
+
 /* only used at mount-time */
 extern struct dentry * d_alloc_root(struct inode *);
 
diff -urN linux-2.4.11-pre4/include/linux/ext2_fs.h linux/include/linux/ext2_fs.h
--- linux-2.4.11-pre4/include/linux/ext2_fs.h	Sun Sep 23 10:31:28 2001
+++ linux/include/linux/ext2_fs.h	Mon Dec  3 23:16:38 2001
@@ -551,9 +551,9 @@
 /* balloc.c */
 extern int ext2_bg_has_super(struct super_block *sb, int group);
 extern unsigned long ext2_bg_num_gdb(struct super_block *sb, int group);
-extern int ext2_new_block (const struct inode *, unsigned long,
+extern int ext2_new_block (struct inode *, unsigned long,
 			   __u32 *, __u32 *, int *);
-extern void ext2_free_blocks (const struct inode *, unsigned long,
+extern void ext2_free_blocks (struct inode *, unsigned long,
 			      unsigned long);
 extern unsigned long ext2_count_free_blocks (struct super_block *);
 extern void ext2_check_blocks_bitmap (struct super_block *);
diff -urN linux-2.4.11-pre4/include/linux/fs.h linux/include/linux/fs.h
--- linux-2.4.11-pre4/include/linux/fs.h	Mon Dec  3 23:16:28 2001
+++ linux/include/linux/fs.h	Mon Dec  3 23:16:38 2001
@@ -108,6 +108,7 @@
 #define MS_NOATIME	1024	/* Do not update access times. */
 #define MS_NODIRATIME	2048	/* Do not update directory access times */
 #define MS_BIND		4096
+#define MS_REC		16384
 #define MS_NOUSER	(1<<31)
 
 /*
@@ -919,11 +920,11 @@
 struct dquot_operations {
 	void (*initialize) (struct inode *, short);
 	void (*drop) (struct inode *);
-	int (*alloc_block) (const struct inode *, unsigned long, char);
+	int (*alloc_block) (struct inode *, unsigned long, char);
 	int (*alloc_inode) (const struct inode *, unsigned long);
-	void (*free_block) (const struct inode *, unsigned long);
+	void (*free_block) (struct inode *, unsigned long);
 	void (*free_inode) (const struct inode *, unsigned long);
-	int (*transfer) (struct dentry *, struct iattr *);
+	int (*transfer) (struct inode *, struct iattr *);
 };
 
 struct file_system_type {
diff -urN linux-2.4.11-pre4/include/linux/module.h linux/include/linux/module.h
--- linux-2.4.11-pre4/include/linux/module.h	Mon Dec  3 23:16:28 2001
+++ linux/include/linux/module.h	Mon Dec  3 23:16:38 2001
@@ -348,6 +348,13 @@
 #define EXPORT_SYMBOL_NOVERS(var)  error config_must_be_included_before_module
 #define EXPORT_SYMBOL_GPL(var)  error config_must_be_included_before_module
 
+#elif !defined(EXPORT_SYMTAB)
+
+#define __EXPORT_SYMBOL(sym,str)   error this_object_must_be_defined_as_export_objs_in_the_Makefile
+#define EXPORT_SYMBOL(var)	   error this_object_must_be_defined_as_export_objs_in_the_Makefile
+#define EXPORT_SYMBOL_NOVERS(var)  error this_object_must_be_defined_as_export_objs_in_the_Makefile
+#define EXPORT_SYMBOL_GPL(var)  error this_object_must_be_defined_as_export_objs_in_the_Makefile
+
 #elif !defined(CONFIG_MODULES)
 
 #define __EXPORT_SYMBOL(sym,str)
diff -urN linux-2.4.11-pre4/include/linux/mount.h linux/include/linux/mount.h
--- linux-2.4.11-pre4/include/linux/mount.h	Wed Sep 12 15:34:06 2001
+++ linux/include/linux/mount.h	Mon Dec  3 23:16:38 2001
@@ -29,7 +29,6 @@
 	int mnt_flags;
 	char *mnt_devname;		/* Name of device e.g. /dev/dsk/hda1 */
 	struct list_head mnt_list;
-	uid_t mnt_owner;
 };
 
 static inline struct vfsmount *mntget(struct vfsmount *mnt)
diff -urN linux-2.4.11-pre4/include/linux/pagemap.h linux/include/linux/pagemap.h
--- linux-2.4.11-pre4/include/linux/pagemap.h	Mon Dec  3 23:16:28 2001
+++ linux/include/linux/pagemap.h	Mon Dec  3 23:16:38 2001
@@ -83,8 +83,6 @@
 #define find_lock_page(mapping, index) \
 	__find_lock_page(mapping, index, page_hash(mapping, index))
 
-extern void __add_page_to_hash_queue(struct page * page, struct page **p);
-
 extern void add_to_page_cache(struct page * page, struct address_space *mapping, unsigned long index);
 extern void add_to_page_cache_locked(struct page * page, struct address_space *mapping, unsigned long index);
 extern int add_to_page_cache_unique(struct page * page, struct address_space *mapping, unsigned long index, struct page **hash);
diff -urN linux-2.4.11-pre4/include/linux/quota.h linux/include/linux/quota.h
--- linux-2.4.11-pre4/include/linux/quota.h	Fri Feb  9 14:46:13 2001
+++ linux/include/linux/quota.h	Mon Dec  3 23:16:38 2001
@@ -81,13 +81,6 @@
 #define QUOTAFILENAME "quota"
 #define QUOTAGROUP "staff"
 
-extern int nr_dquots, nr_free_dquots;
-extern int max_dquots;
-extern int dquot_root_squash;
-
-#define NR_DQHASH 43            /* Just an arbitrary number */
-#define NR_DQUOTS 1024          /* Maximum number of quotas active at one time (Configurable from /proc/sys/fs) */
-
 /*
  * Command definitions for the 'quotactl' system call.
  * The commands are broken into a main command defined below
@@ -151,26 +144,23 @@
 
 #ifdef __KERNEL__
 
-/*
- * Maximum length of a message generated in the quota system,
- * that needs to be kicked onto the tty.
- */
-#define MAX_QUOTA_MESSAGE 75
+extern int nr_dquots, nr_free_dquots;
+extern int dquot_root_squash;
+
+#define NR_DQHASH 43            /* Just an arbitrary number */
 
-#define DQ_LOCKED     0x01	/* locked for update */
-#define DQ_WANT       0x02	/* wanted for update */
-#define DQ_MOD        0x04	/* dquot modified since read */
+#define DQ_LOCKED     0x01	/* dquot under IO */
+#define DQ_MOD        0x02	/* dquot modified since read */
 #define DQ_BLKS       0x10	/* uid/gid has been warned about blk limit */
 #define DQ_INODES     0x20	/* uid/gid has been warned about inode limit */
 #define DQ_FAKE       0x40	/* no limits only usage */
 
 struct dquot {
-	struct dquot *dq_next;		/* Pointer to next dquot */
-	struct dquot **dq_pprev;
-	struct list_head dq_free;	/* free list element */
-	struct dquot *dq_hash_next;	/* Pointer to next in dquot_hash */
-	struct dquot **dq_hash_pprev;	/* Pointer to previous in dquot_hash */
-	wait_queue_head_t dq_wait;	/* Pointer to waitqueue */
+	struct list_head dq_hash;	/* Hash list in memory */
+	struct list_head dq_inuse;	/* List of all quotas */
+	struct list_head dq_free;	/* Free list element */
+	wait_queue_head_t dq_wait_lock;	/* Pointer to waitqueue on dquot lock */
+	wait_queue_head_t dq_wait_free;	/* Pointer to waitqueue for quota to be unused */
 	int dq_count;			/* Reference count */
 
 	/* fields after this point are cleared when invalidating */
@@ -189,7 +179,6 @@
 /*
  * Flags used for set_dqblk.
  */
-#define QUOTA_SYSCALL     0x01
 #define SET_QUOTA         0x02
 #define SET_USE           0x04
 #define SET_QLIMIT        0x08
diff -urN linux-2.4.11-pre4/include/linux/quotaops.h linux/include/linux/quotaops.h
--- linux-2.4.11-pre4/include/linux/quotaops.h	Sun Sep 23 10:31:28 2001
+++ linux/include/linux/quotaops.h	Mon Dec  3 23:16:38 2001
@@ -11,10 +11,11 @@
 #define _LINUX_QUOTAOPS_
 
 #include <linux/config.h>
+#include <linux/smp_lock.h>
 
 #if defined(CONFIG_QUOTA)
 
-#include <linux/smp_lock.h>
+#include <linux/fs.h>
 
 /*
  * declaration of quota_function calls in kernel.
@@ -24,85 +25,138 @@
 extern int  quota_off(struct super_block *sb, short type);
 extern int  sync_dquots(kdev_t dev, short type);
 
-extern int  dquot_alloc_block(const struct inode *inode, unsigned long number, char prealloc);
+extern int  dquot_alloc_block(struct inode *inode, unsigned long number, char prealloc);
 extern int  dquot_alloc_inode(const struct inode *inode, unsigned long number);
 
-extern void dquot_free_block(const struct inode *inode, unsigned long number);
+extern void dquot_free_block(struct inode *inode, unsigned long number);
 extern void dquot_free_inode(const struct inode *inode, unsigned long number);
 
-extern int  dquot_transfer(struct dentry *dentry, struct iattr *iattr);
+extern int  dquot_transfer(struct inode *inode, struct iattr *iattr);
 
 /*
  * Operations supported for diskquotas.
  */
+#define sb_any_quota_enabled(sb) ((sb)->s_dquot.flags & (DQUOT_USR_ENABLED | DQUOT_GRP_ENABLED))
+
 static __inline__ void DQUOT_INIT(struct inode *inode)
 {
-	if (!IS_NOQUOTA(inode)) {
-		if (inode->i_sb && inode->i_sb->dq_op)
-			inode->i_sb->dq_op->initialize(inode, -1);
-	}
+	if (!inode->i_sb)
+		BUG();
+	lock_kernel();
+	if (sb_any_quota_enabled(inode->i_sb) && !IS_NOQUOTA(inode))
+		inode->i_sb->dq_op->initialize(inode, -1);
+	unlock_kernel();
 }
 
 static __inline__ void DQUOT_DROP(struct inode *inode)
 {
+	lock_kernel();
 	if (IS_QUOTAINIT(inode)) {
-		if (inode->i_sb && inode->i_sb->dq_op)
-			inode->i_sb->dq_op->drop(inode);
+		if (!inode->i_sb)
+			BUG();
+		inode->i_sb->dq_op->drop(inode);	/* Ops must be set when there's any quota... */
 	}
+	unlock_kernel();
 }
 
-static __inline__ int DQUOT_PREALLOC_BLOCK(struct super_block *sb, const struct inode *inode, int nr)
+static __inline__ int DQUOT_PREALLOC_BLOCK_NODIRTY(struct inode *inode, int nr)
 {
-	if (sb->dq_op) {
-		if (sb->dq_op->alloc_block(inode, fs_to_dq_blocks(nr, sb->s_blocksize), 1) == NO_QUOTA)
+	lock_kernel();
+	if (sb_any_quota_enabled(inode->i_sb)) {
+		/* Number of used blocks is updated in alloc_block() */
+		if (inode->i_sb->dq_op->alloc_block(inode, fs_to_dq_blocks(nr, inode->i_sb->s_blocksize), 1) == NO_QUOTA) {
+			unlock_kernel();
 			return 1;
+		}
 	}
+	else
+		inode->i_blocks += nr << (inode->i_sb->s_blocksize_bits - 9);
+	unlock_kernel();
 	return 0;
 }
 
-static __inline__ int DQUOT_ALLOC_BLOCK(struct super_block *sb, const struct inode *inode, int nr)
+static __inline__ int DQUOT_PREALLOC_BLOCK(struct inode *inode, int nr)
 {
-	if (sb->dq_op) {
-		if (sb->dq_op->alloc_block(inode, fs_to_dq_blocks(nr, sb->s_blocksize), 0) == NO_QUOTA)
+	int ret;
+        if (!(ret =  DQUOT_PREALLOC_BLOCK_NODIRTY(inode, nr)))
+		mark_inode_dirty(inode);
+	return ret;
+}
+
+static __inline__ int DQUOT_ALLOC_BLOCK_NODIRTY(struct inode *inode, int nr)
+{
+	lock_kernel();
+	if (sb_any_quota_enabled(inode->i_sb)) {
+		/* Number of used blocks is updated in alloc_block() */
+		if (inode->i_sb->dq_op->alloc_block(inode, fs_to_dq_blocks(nr, inode->i_sb->s_blocksize), 0) == NO_QUOTA) {
+			unlock_kernel();
 			return 1;
+		}
 	}
+	else
+		inode->i_blocks += nr << (inode->i_sb->s_blocksize_bits - 9);
+	unlock_kernel();
 	return 0;
 }
 
-static __inline__ int DQUOT_ALLOC_INODE(struct super_block *sb, struct inode *inode)
+static __inline__ int DQUOT_ALLOC_BLOCK(struct inode *inode, int nr)
+{
+	int ret;
+	if (!(ret = DQUOT_ALLOC_BLOCK_NODIRTY(inode, nr)))
+		mark_inode_dirty(inode);
+	return ret;
+}
+
+static __inline__ int DQUOT_ALLOC_INODE(struct inode *inode)
 {
-	if (sb->dq_op) {
+	lock_kernel();
+	if (sb_any_quota_enabled(inode->i_sb)) {
 		DQUOT_INIT(inode);
-		if (sb->dq_op->alloc_inode (inode, 1))
+		if (inode->i_sb->dq_op->alloc_inode(inode, 1) == NO_QUOTA) {
+			unlock_kernel();
 			return 1;
+		}
 	}
-	inode->i_flags |= S_QUOTA;
+	unlock_kernel();
 	return 0;
 }
 
-static __inline__ void DQUOT_FREE_BLOCK(struct super_block *sb, const struct inode *inode, int nr)
+static __inline__ void DQUOT_FREE_BLOCK_NODIRTY(struct inode *inode, int nr)
 {
-	if (sb->dq_op)
-		sb->dq_op->free_block(inode, fs_to_dq_blocks(nr, sb->s_blocksize));
+	lock_kernel();
+	if (sb_any_quota_enabled(inode->i_sb))
+		inode->i_sb->dq_op->free_block(inode, fs_to_dq_blocks(nr, inode->i_sb->s_blocksize));
+	else
+		inode->i_blocks -= nr << (inode->i_sb->s_blocksize_bits - 9);
+	unlock_kernel();
 }
 
-static __inline__ void DQUOT_FREE_INODE(struct super_block *sb, struct inode *inode)
+static __inline__ void DQUOT_FREE_BLOCK(struct inode *inode, int nr)
 {
-	if (sb->dq_op)
-		sb->dq_op->free_inode(inode, 1);
+	DQUOT_FREE_BLOCK_NODIRTY(inode, nr);
+	mark_inode_dirty(inode);
 }
 
-static __inline__ int DQUOT_TRANSFER(struct dentry *dentry, struct iattr *iattr)
+static __inline__ void DQUOT_FREE_INODE(struct inode *inode)
 {
-	int error = -EDQUOT;
+	lock_kernel();
+	if (sb_any_quota_enabled(inode->i_sb))
+		inode->i_sb->dq_op->free_inode(inode, 1);
+	unlock_kernel();
+}
 
-	if (dentry->d_inode->i_sb->dq_op && !IS_NOQUOTA(dentry->d_inode)) {
-		DQUOT_INIT(dentry->d_inode);
-		error = dentry->d_inode->i_sb->dq_op->transfer(dentry, iattr);
-	} else {
-		error = notify_change(dentry, iattr);
+static __inline__ int DQUOT_TRANSFER(struct inode *inode, struct iattr *iattr)
+{
+	lock_kernel();
+	if (sb_any_quota_enabled(inode->i_sb) && !IS_NOQUOTA(inode)) {
+		DQUOT_INIT(inode);
+		if (inode->i_sb->dq_op->transfer(inode, iattr) == NO_QUOTA) {
+			unlock_kernel();
+			return 1;
+		}
 	}
-	return error;
+	unlock_kernel();
+	return 0;
 }
 
 #define DQUOT_SYNC(dev)	sync_dquots(dev, -1)
@@ -115,18 +169,53 @@
  */
 #define DQUOT_INIT(inode)			do { } while(0)
 #define DQUOT_DROP(inode)			do { } while(0)
-#define DQUOT_PREALLOC_BLOCK(sb, inode, nr)	(0)
-#define DQUOT_ALLOC_BLOCK(sb, inode, nr)	(0)
-#define DQUOT_ALLOC_INODE(sb, inode)		(0)
-#define DQUOT_FREE_BLOCK(sb, inode, nr)		do { } while(0)
-#define DQUOT_FREE_INODE(sb, inode)		do { } while(0)
+#define DQUOT_ALLOC_INODE(inode)		(0)
+#define DQUOT_FREE_INODE(inode)			do { } while(0)
 #define DQUOT_SYNC(dev)				do { } while(0)
 #define DQUOT_OFF(sb)				do { } while(0)
+#define DQUOT_TRANSFER(inode, iattr)		(0)
+extern __inline__ int DQUOT_PREALLOC_BLOCK_NODIRTY(struct inode *inode, int nr)
+{
+	lock_kernel();
+	inode->i_blocks += nr << (inode->i_sb->s_blocksize_bits - 9);
+	unlock_kernel();
+	return 0;
+}
 
-/*
- * Special case expands to a simple notify_change.
- */
-#define DQUOT_TRANSFER(dentry, iattr) notify_change(dentry, iattr)
+extern __inline__ int DQUOT_PREALLOC_BLOCK(struct inode *inode, int nr)
+{
+	DQUOT_PREALLOC_BLOCK_NODIRTY(inode, nr);
+	mark_inode_dirty(inode);
+	return 0;
+}
+
+extern __inline__ int DQUOT_ALLOC_BLOCK_NODIRTY(struct inode *inode, int nr)
+{
+	lock_kernel();
+	inode->i_blocks += nr << (inode->i_sb->s_blocksize_bits - 9);
+	unlock_kernel();
+	return 0;
+}
+
+extern __inline__ int DQUOT_ALLOC_BLOCK(struct inode *inode, int nr)
+{
+	DQUOT_ALLOC_BLOCK_NODIRTY(inode, nr);
+	mark_inode_dirty(inode);
+	return 0;
+}
+
+extern __inline__ void DQUOT_FREE_BLOCK_NODIRTY(struct inode *inode, int nr)
+{
+	lock_kernel();
+	inode->i_blocks -= nr << (inode->i_sb->s_blocksize_bits - 9);
+	unlock_kernel();
+}
+
+extern __inline__ void DQUOT_FREE_BLOCK(struct inode *inode, int nr)
+{
+	DQUOT_FREE_BLOCK_NODIRTY(inode, nr);
+	mark_inode_dirty(inode);
+}	
 
 #endif /* CONFIG_QUOTA */
 #endif /* _LINUX_QUOTAOPS_ */
diff -urN linux-2.4.11-pre4/include/net/route.h linux/include/net/route.h
--- linux-2.4.11-pre4/include/net/route.h	Sun Sep 23 10:31:33 2001
+++ linux/include/net/route.h	Mon Dec  3 23:16:38 2001
@@ -30,6 +30,7 @@
 #include <linux/in_route.h>
 #include <linux/rtnetlink.h>
 #include <linux/route.h>
+#include <linux/cache.h>
 
 #ifndef __KERNEL__
 #warning This file is not supposed to be used outside of kernel.
@@ -105,7 +106,7 @@
         unsigned int out_hit;
         unsigned int out_slow_tot;
         unsigned int out_slow_mc;
-};
+} ____cacheline_aligned_in_smp;
 
 extern struct ip_rt_acct *ip_rt_acct;
 
diff -urN linux-2.4.11-pre4/init/main.c linux/init/main.c
--- linux-2.4.11-pre4/init/main.c	Mon Dec  3 23:16:28 2001
+++ linux/init/main.c	Mon Dec  3 23:16:38 2001
@@ -482,7 +482,7 @@
 extern void setup_arch(char **);
 extern void cpu_idle(void);
 
-volatile unsigned long wait_init_idle = 0UL;
+unsigned long wait_init_idle;
 
 #ifndef CONFIG_SMP
 
@@ -505,18 +505,18 @@
 	smp_boot_cpus();
 	wait_init_idle = cpu_online_map;
 	clear_bit(current->processor, &wait_init_idle); /* Don't wait on me! */
-	printk("Waiting on wait_init_idle (map = 0x%lx)\n", wait_init_idle);
+
 	smp_threads_ready=1;
 	smp_commence();
 
 	/* Wait for the other cpus to set up their idle processes */
-        while (1) {
-                if (!wait_init_idle)
-                        break;
-                rep_nop();
-        }
+	printk("Waiting on wait_init_idle (map = 0x%lx)\n", wait_init_idle);
+	while (wait_init_idle) {
+		cpu_relax();
+		barrier();
+	}
 	printk("All processors have done init_idle\n");
-}		
+}
 
 #endif
 
diff -urN linux-2.4.11-pre4/kernel/sched.c linux/kernel/sched.c
--- linux-2.4.11-pre4/kernel/sched.c	Mon Dec  3 23:16:28 2001
+++ linux/kernel/sched.c	Mon Dec  3 23:16:38 2001
@@ -1309,7 +1309,7 @@
 	atomic_inc(&current->files->count);
 }
 
-extern volatile unsigned long wait_init_idle;
+extern unsigned long wait_init_idle;
 
 void __init init_idle(void)
 {
diff -urN linux-2.4.11-pre4/kernel/sysctl.c linux/kernel/sysctl.c
--- linux-2.4.11-pre4/kernel/sysctl.c	Mon Dec  3 23:16:28 2001
+++ linux/kernel/sysctl.c	Mon Dec  3 23:16:38 2001
@@ -288,8 +288,6 @@
 	 0644, NULL, &proc_dointvec},
 	{FS_NRDQUOT, "dquot-nr", &nr_dquots, 2*sizeof(int),
 	 0444, NULL, &proc_dointvec},
-	{FS_MAXDQUOT, "dquot-max", &max_dquots, sizeof(int),
-	 0644, NULL, &proc_dointvec},
 	{FS_DENTRY, "dentry-state", &dentry_stat, 6*sizeof(int),
 	 0444, NULL, &proc_dointvec},
 	{FS_OVERFLOWUID, "overflowuid", &fs_overflowuid, sizeof(int), 0644, NULL,
diff -urN linux-2.4.11-pre4/mm/oom_kill.c linux/mm/oom_kill.c
--- linux-2.4.11-pre4/mm/oom_kill.c	Mon Dec  3 23:16:28 2001
+++ linux/mm/oom_kill.c	Mon Dec  3 23:16:38 2001
@@ -241,13 +241,12 @@
 		return 0;
 
 	/*
-	 * If the buffer and page cache (excluding swap cache) are over
+	 * If the buffer and page cache (including swap cache) are over
 	 * their (/proc tunable) minimum, we're still not OOM.  We test
 	 * this to make sure we don't return OOM when the system simply
 	 * has a hard time with the cache.
 	 */
 	cache_mem = atomic_read(&page_cache_size);
-	cache_mem -= swapper_space.nrpages;
 	limit = 2;
 	limit *= num_physpages / 100;
 
diff -urN linux-2.4.11-pre4/mm/page_alloc.c linux/mm/page_alloc.c
--- linux-2.4.11-pre4/mm/page_alloc.c	Mon Dec  3 23:16:28 2001
+++ linux/mm/page_alloc.c	Mon Dec  3 23:16:38 2001
@@ -357,6 +357,7 @@
 
 	/* here we're in the low on memory slow path */
 
+rebalance:
 	if (current->flags & PF_MEMALLOC) {
 		zone = zonelist->zones;
 		for (;;) {
@@ -371,48 +372,28 @@
 		return NULL;
 	}
 
- rebalance:
 	page = balance_classzone(classzone, gfp_mask, order, &freed);
 	if (page)
 		return page;
 
 	zone = zonelist->zones;
-	if (likely(freed)) {
-		for (;;) {
-			zone_t *z = *(zone++);
-			if (!z)
-				break;
+	for (;;) {
+		zone_t *z = *(zone++);
+		if (!z)
+			break;
 
-			if (zone_free_pages(z, order) > z->pages_min) {
-				page = rmqueue(z, order);
-				if (page)
-					return page;
-			}
-		}
-		goto rebalance;
-	} else {
-		/* 
-		 * Check that no other task is been killed meanwhile,
-		 * in such a case we can succeed the allocation.
-		 */
-		for (;;) {
-			zone_t *z = *(zone++);
-			if (!z)
-				break;
-
-			if (zone_free_pages(z, order) > z->pages_min) {
-				page = rmqueue(z, order);
-				if (page)
-					return page;
-			}
+		if (zone_free_pages(z, order) > z->pages_min) {
+			page = rmqueue(z, order);
+			if (page)
+				return page;
 		}
-		
-		goto rebalance;
 	}
 
-	printk(KERN_NOTICE "__alloc_pages: %u-order allocation failed (gfp=0x%x/%i) from %p\n",
-	       order, gfp_mask, !!(current->flags & PF_MEMALLOC), __builtin_return_address(0));
-	return NULL;
+	/* Yield for kswapd, and try again */
+	current->policy |= SCHED_YIELD;
+	__set_current_state(TASK_RUNNING);
+	schedule();
+	goto rebalance;
 }
 
 /*
diff -urN linux-2.4.11-pre4/mm/vmscan.c linux/mm/vmscan.c
--- linux-2.4.11-pre4/mm/vmscan.c	Mon Dec  3 23:16:28 2001
+++ linux/mm/vmscan.c	Mon Dec  3 23:16:38 2001
@@ -546,6 +546,9 @@
 
 	shrink_dcache_memory(priority, gfp_mask);
 	shrink_icache_memory(priority, gfp_mask);
+#ifdef CONFIG_QUOTA
+	shrink_dqcache_memory(DEF_PRIORITY, gfp_mask);
+#endif
 
 	return nr_pages;
 }
@@ -553,14 +556,16 @@
 int try_to_free_pages(zone_t * classzone, unsigned int gfp_mask, unsigned int order)
 {
 	int ret = 0;
+	int priority = DEF_PRIORITY;
 	int nr_pages = SWAP_CLUSTER_MAX;
 
-	nr_pages = shrink_caches(DEF_PRIORITY, classzone, gfp_mask, nr_pages);
+	do {
+		nr_pages = shrink_caches(priority, classzone, gfp_mask, nr_pages);
+		if (nr_pages <= 0)
+			return 1;
 
-	if (nr_pages < SWAP_CLUSTER_MAX)
-		ret |= 1;
-
-	ret |= swap_out(DEF_PRIORITY, classzone, gfp_mask, SWAP_CLUSTER_MAX << 2);
+		ret |= swap_out(priority, classzone, gfp_mask, SWAP_CLUSTER_MAX << 2);
+	} while (--priority);
 
 	return ret;
 }
diff -urN linux-2.4.11-pre4/net/core/dev.c linux/net/core/dev.c
--- linux-2.4.11-pre4/net/core/dev.c	Mon Dec  3 23:16:28 2001
+++ linux/net/core/dev.c	Mon Dec  3 23:16:38 2001
@@ -552,35 +552,22 @@
 {
 	int i;
 	char buf[32];
-	char *p, c;
+	char *p;
 
 	/*
 	 * Verify the string as this thing may have come from
 	 * the user.  There must be one "%d" and no other "%"
 	 * characters.
 	 */
-	p = name;
-	while ((c = *p++) != 0) {
-		if (c == '%') {
-			c = *p++;
-			if (c != 'd')
-				return -EINVAL;
+	p = strchr(name, '%');
+	if (!p || p[1] != 'd' || strchr(p+2, '%'))
+		return -EINVAL;
 
-			while ((c = *p++) != 0) {
-				if (c == '%')
-					return -EINVAL;
-			}
-			goto name_ok;
-		}
-	}
-	return -EINVAL;
-
- name_ok:
 	/*
 	 * If you need over 100 please also fix the algorithm...
 	 */
 	for (i = 0; i < 100; i++) {
-		sprintf(buf,name,i);
+		snprintf(buf,sizeof(buf),name,i);
 		if (__dev_get_by_name(buf) == NULL) {
 			strcpy(dev->name, buf);
 			return i;
diff -urN linux-2.4.11-pre4/net/ipv4/netfilter/ipt_REJECT.c linux/net/ipv4/netfilter/ipt_REJECT.c
--- linux-2.4.11-pre4/net/ipv4/netfilter/ipt_REJECT.c	Mon Dec  3 23:16:28 2001
+++ linux/net/ipv4/netfilter/ipt_REJECT.c	Mon Dec  3 23:16:38 2001
@@ -325,7 +325,7 @@
 {
  	const struct ipt_reject_info *rejinfo = targinfo;
 
- 	if (targinfosize != IPT_ALIGN(sizeof(struct ipt_icmp))) {
+ 	if (targinfosize != IPT_ALIGN(sizeof(struct ipt_reject_info))) {
   		DEBUGP("REJECT: targinfosize %u != 0\n", targinfosize);
   		return 0;
   	}
diff -urN linux-2.4.11-pre4/net/ipv4/netfilter/ipt_mac.c linux/net/ipv4/netfilter/ipt_mac.c
--- linux-2.4.11-pre4/net/ipv4/netfilter/ipt_mac.c	Mon Dec  3 23:16:28 2001
+++ linux/net/ipv4/netfilter/ipt_mac.c	Mon Dec  3 23:16:38 2001
@@ -20,7 +20,7 @@
 
     /* Is mac pointer valid? */
     return (skb->mac.raw >= skb->head
-	    && skb->mac.raw < skb->head + skb->len - ETH_HLEN
+	    && (skb->mac.raw + ETH_HLEN) <= skb->data
 	    /* If so, compare... */
 	    && ((memcmp(skb->mac.ethernet->h_source, info->srcaddr, ETH_ALEN)
 		== 0) ^ info->invert));
diff -urN linux-2.4.11-pre4/scripts/kernel-doc linux/scripts/kernel-doc
--- linux-2.4.11-pre4/scripts/kernel-doc	Mon Dec  3 23:16:29 2001
+++ linux/scripts/kernel-doc	Mon Dec  3 23:16:38 2001
@@ -588,7 +588,7 @@
     } else {
 	print "  <void>\n";
     }
-    print "  </funcsynopsis>\n";
+    print "  </funcprototype></funcsynopsis>\n";
     print "</refsynopsisdiv>\n";
 
     # print parameters
