## Automatically generated incremental diff
## From:   linux-2.5.2-pre7
## To:     linux-2.5.2-pre8
## Robot:  $Id: make-incremental-diff,v 1.9 2001/12/10 00:06:56 hpa Exp $

diff -urN linux-2.5.2-pre7/Documentation/usb/ov511.txt linux/Documentation/usb/ov511.txt
--- linux-2.5.2-pre7/Documentation/usb/ov511.txt	Sun Nov 12 20:45:18 2000
+++ linux/Documentation/usb/ov511.txt	Fri Jan  4 19:01:19 2002
@@ -8,11 +8,11 @@
 INTRODUCTION:
 
 This is a driver for the OV511, a USB-only chip used in many "webcam" devices.
-Any camera using the OV511/OV511+ and the OV7610/20/20AE CCD should work. It 
+Any camera using the OV511/OV511+ and the OV6620/OV7610/20/20AE should work.
+Video capture devices that use the Philips SAA7111A decoder also work. It 
 supports streaming and capture of color or monochrome video via the Video4Linux
-API. Most V4L apps are compatible with it, but a few video-conferencing programs
-do not work yet. The following resolutions are supported: 640x480, 448x336,
-384x288, 352x288, and 320x240.
+API. Most V4L apps are compatible with it. Most resolutions with a width and
+height that are a multiple of 8 are supported.
 
 If you need more information, please visit the OV511 homepage at the above URL.
 
@@ -27,22 +27,25 @@
 
 HOW TO USE IT:
 
+Note: These are simplified instructions. For complete instructions see:
+	http://alpha.dyndns.org/ov511/install.html
+
 You must have first compiled USB support, support for your specific USB host
 controller (UHCI or OHCI), and Video4Linux support for your kernel (I recommend
-making them modules.)
+making them modules.) Make sure "Enforce bandwidth allocation" is NOT enabled.
 
-Next, (as root) from your appropriate modules directory (lib/modules/2.3.XX):
+Next, (as root):
 
-	insmod usb/usbcore.o
-	insmod usb/usb-uhci.o  <OR>  insmod usb/ohci-hcd.o
-	insmod misc/videodev.o
-	insmod usb/ov511.o
+	modprobe usbcore
+	modprobe usb-uhci  <OR>  modprobe usb-ohci
+	modprobe videodev
+	modprobe ov511
 
 If it is not already there (it usually is), create the video device:
 
-	mknod /dev/video c 81 0
+	mknod /dev/video0 c 81 0
 
-Sometimes /dev/video is a symlink to /dev/video0
+Optionally, symlink /dev/video to /dev/video0
 
 You will have to set permissions on this device to allow you to read/write
 from it:
@@ -55,39 +58,40 @@
 	
 [Using vidcat:]
 
-	vidcat -s 640x480 > test.jpg
+	vidcat -s 640x480 -p c > test.jpg
 	xview test.jpg
 	
 [Using xawtv:]
 
-You must make some modifications to the source and compile it before you use it.
-(Note: this may not be applicable to versions other than 3.06)
-
-In src/Xawtv.ad, change xawtv.tv.width to 640 and xawtv.tv.height to 480. Next,
-in src/grab-v4l.c, change SYNC_TIMEOUT from 1 to 2. Then, from the main xawtv
-directory:
+From the main xawtv directory:
 
 	make clean
 	./configure
 	make
 	make install
 
-Now you should be able to run xawtv. Right click for the options dialog. If
-you get a scrambled image it is likely that you made a mistake in Xawtv.ad.
-Try setting the size to 320x240 if all else fails.
+Now you should be able to run xawtv. Right click for the options dialog. 
 
 MODULE PARAMETERS:
 
   You can set these with:  insmod ov511 NAME=VALUE
   There is currently no way to set these on a per-camera basis.
 
-  NAME: autoadjust
-  TYPE: integer (boolean)
+  NAME: autobright
+  TYPE: integer (Boolean)
   DEFAULT: 1
-  DESC: The camera normally adjusts exposure, gain, and hue automatically. This
-        can be set to 0 to disable this automatic adjustment. Note that there is
-        currently no way to set these parameters manually once autoadjust is
-        disabled.
+  DESC: Brightness is normally under automatic control and can't be set
+        manually by the video app. Set to 0 for manual control.
+
+  NAME: autogain
+  TYPE: integer (Boolean)
+  DEFAULT: 1
+  DESC: Auto Gain Control enable. This feature is not yet implemented.
+
+  NAME: autoexp
+  TYPE: integer (Boolean)
+  DEFAULT: 1
+  DESC: Auto Exposure Control enable. This feature is not yet implemented.
 
   NAME: debug
   TYPE: integer (0-6)
@@ -102,49 +106,23 @@
           5=highly repetitive mesgs
 
   NAME: fix_rgb_offset
-  TYPE: integer (boolean)
+  TYPE: integer (Boolean)
   DEFAULT: 0
   DESC: Some people have reported that the blue component of the image is one
         or so lines higher than the red component. This is only apparent in 
         images with white objects on black backgrounds at 640x480. Setting this
-        to 1 will realign the color planes correctly. NOTE: This is still
-        experimental and very buggy. You will likely need a fast (500 MHz) CPU.
+        to 1 will realign the color planes correctly. NOTE: You will likely
+        need a fast (500 MHz) CPU.
 
   NAME: snapshot
-  TYPE: integer (boolean)
+  TYPE: integer (Boolean)
   DEFAULT: 0
-  DESC: Set to 1 to enable snapshot mode. read() will block until the snapshot
-        button is pressed. Note that this does not yet work with most apps,
-        including xawtv and vidcat. NOTE: See the section "TODO" for more info.
-
-  NAME: sensor
-  TYPE: integer ([0, 1, 3])
-  DEFAULT: [varies]
-  DESC: If you know that your camera sensor is not detected correctly, set this
-        parameter. This is a global option for all attached OV511 cameras. You
-        will probably never need to set this, but if you do, valid values are:
-        	0 for OV7620
-        	1 for OV7620AE
-        	3 for OV7610
-
-  NAME: i2c_detect_tries
-  TYPE: integer (don't set it insanely high!)
-  DEFAULT: 5
-  DESC: This is the number of times the driver will try to sync and detect the
-        internal i2c bus (which connects the OV511 and sensor). If you are
-        getting intermittent detection failures ("Failed to read sensor ID...")
-        you should increase this by a modest amount. If setting it to 20 or so
-        doesn't fix things, look elsewhere for the cause of the problem.
-
-  NAME: aperture
-  TYPE: integer (0 - 15)
-  DEFAULT: [varies by sensor]
-  DESC: For legal values, see the OV7610/7620 specs under register Common F.
-        This setting affects the upper nybble of that reg (bits 4-7). This is
-        for if you want to play with the camera's pixel saturation.
+  DESC: Set to 1 to enable snapshot mode. read()/VIDIOCSYNC will block until
+	the snapshot button is pressed. Note: enabling this mode disables
+	/proc/video/ov511/<minor#>/button
 
-  NAME: force_rgb
-  TYPE: integer (boolean)
+  NAME: force_rgb	(Deprecated; may be removed in the future)
+  TYPE: integer (Boolean)
   DEFAULT: 0
   DESC: Force image to be read in RGB instead of BGR. This option allow
         programs that expect RGB data (e.g. gqcam) to work with this driver. If
@@ -169,60 +147,179 @@
         both OV511 and OV511+ cameras, trial-and-error may be necessary for
         finding the optimum setting.
 
-  NAME: retry_sync
-  TYPE: boolean
+  NAME: compress
+  TYPE: integer (Boolean)
+  DEFAULT: 0
+  DESC: Set this to 1 to turn on the camera's compression engine. This can
+        potentially increase the frame rate at the expense of quality, if you
+        have a fast CPU. You must load the proper compression module for your
+        camera before starting your application (ov511_decomp or ov518_decomp).
+
+  NAME: testpat
+  TYPE: integer (Boolean)
   DEFAULT: 0
-  DESC: Prevent apps from timing out if frame is not done in time. This is
-        useful if you are having problems with Xawtv getting "stuck" on a frame
-        when your system is under heavy load.
+  DESC: This configures the camera's sensor to transmit a colored test-pattern
+        instead of an image. This does not work correctly yet.
 
-  NAME: sensor_gbr
-  TYPE: boolean
+  NAME: sensor_gbr (*** TEMPORARILY DISABLED ***)
+  TYPE: integer (Boolean)
   DEFAULT: 0
   DESC: This makes the sensor output GBR422 instead of YUV420. This saves the
         driver the trouble of converting YUV to RGB, but it currently does not
         work very well (the colors are not quite right)
 
+  NAME: dumppix
+  TYPE: integer (0-2)
+  DEFAULT: 0
+  DESC: Dumps raw pixel data and skips post-processing and format conversion.
+	It is for debugging purposes only. Options are:
+		0: Disable (default)
+		1: Dump raw data from camera, excluding headers and trailers
+		2: Dumps data exactly as received from camera
+
+  NAME: led
+  TYPE: integer (0-2)
+  DEFAULT: 1 (Always on)
+  DESC: Controls whether the LED (the little light) on the front of the camera
+	is always off (0), always on (1), or only on when driver is open (2).
+	This is only supported with the OV511+ chipset, and even then only on
+	some cameras (ones that actually have the LED wired to the control pin,
+	and not just hardwired to be on all the time).
+
+  NAME: dump_bridge
+  TYPE: integer (Boolean)
+  DEFAULT: 0
+  DESC: Dumps the bridge (OV511[+] or OV518[+]) register values to the system
+	log. Only useful for serious debugging/development purposes.
+
+  NAME: dump_sensor
+  TYPE: integer (Boolean)
+  DEFAULT: 0
+  DESC: Dumps the sensor register values to the system log. Only useful for
+	serious debugging/development purposes.
+
+  NAME: printph
+  TYPE: integer (Boolean)
+  DEFAULT: 0
+  DESC: Setting this to 1 will dump the first 12 bytes of each isoc frame. This
+	is only useful if you are trying to debug problems with the isoc data
+	stream (i.e.: camera initializes, but vidcat hangs until Ctrl-C). Be
+	warned that this dumps a large number of messages to your kernel log.
+
+  NAME: phy, phuv, pvy, pvuv, qhy, qhuv, qvy, qvuv
+  TYPE: integer (0-63 for phy and phuv, 0-255 for rest)
+  DEFAULT: OV511 default values
+  DESC: These are registers 70h - 77h of the OV511, which control the
+	prediction ranges and quantization thresholds of the compressor, for
+	the Y and UV channels in the horizontal and vertical directions. See
+	the OV511 or OV511+ data sheet for more detailed descriptions. These
+	normally do not need to be changed.
+
+  NAME: lightfreq
+  TYPE: integer (0, 50, or 60)
+  DEFAULT: 0 (use sensor default)
+  DESC: Sets the sensor to match your lighting frequency. This can reduce the
+	appearance of "banding", i.e. horizontal lines or waves of light and
+	dark that are often caused by artificial lighting. Valid values are:
+		0 - Use default (depends on sensor, most likely 60 Hz)
+		50 - For European and Asian 50 Hz power
+		60 - For American 60 Hz power
+
+  NAME: bandingfilter
+  TYPE: integer (Boolean)
+  DEFAULT: 0 (off)
+  DESC: Enables the sensorīs banding filter exposure algorithm. This reduces
+	or stabilizes the "banding" caused by some artificial light sources
+	(especially fluorescent). You might have to set lightfreq correctly for
+	this to work right. As an added bonus, this sometimes makes it
+	possible to capture your monitorīs output.
+
+  NAME: fastset
+  TYPE: integer (Boolean)
+  DEFAULT: 0 (off)
+  DESC: Allows picture settings (brightness, contrast, color, and hue) to take
+	effect immediately, even in the middle of a frame. This reduces the
+	time to change settings, but can ruin frames during the change. Only
+	affects OmniVision sensors.
+
+  NAME: force_palette
+  TYPE: integer (Boolean)
+  DEFAULT: 0 (off)
+  DESC: Forces the palette (color format) to a specific value. If an
+	application requests a different palette, it will be rejected, thereby
+	forcing it to try others until it succeeds. This is useful for forcing
+	greyscale mode with a color camera, for example. Supported modes are:
+		0                           (Allows all the following formats)
+		1   VIDEO_PALETTE_GREY      (Linear greyscale)
+		3   VIDEO_PALETTE_RGB565    (565 16 bit RGB)
+		4   VIDEO_PALETTE_RGB24     (24bit RGB)
+		7   VIDEO_PALETTE_YUV422    (YUV422 capture)
+		8   VIDEO_PALETTE_YUYV      (YUV422 capture; same as 7)
+		10  VIDEO_PALETTE_YUV420    (YUV 4:2:0 Planar)
+		13  VIDEO_PALETTE_YUV422P   (YUV 4:2:2 Planar)
+		15  VIDEO_PALETTE_YUV420P   (YUV 4:2:0 Planar, same as 10)
+
+  NAME: tuner
+  TYPE: integer
+  DEFAULT: -1 (autodetect)
+  DESC: This sets the exact type of the tuner module in a device. This is set
+	automatically based on the custom ID of the OV511 device. In cases
+	where this fails, you can override this auto-detection. Please see
+	linux/drivers/media/video/tuner.h for a complete list.
+
+  NAME: backlight
+  TYPE: integer (Boolean)
+  DEFAULT: 0 (off)
+  DESC: Setting this flag changes the exposure algorithm for OmniVision sensors
+	such that objects in the camera's view (i.e. your head) can be clearly
+	seen when they are illuminated from behind. It reduces or eliminates
+	the sensor's auto-exposure function, so it should only be used when
+	needed. Additionally, it is only supported with the OV6620 and OV7620.
+
+  NAME: unit_video
+  TYPE: Up to 16 comma-separated integers
+  DEFAULT: 0,0,0... (automatically assign the next available minor(s))
+  DESC: You can specify up to 16 minor numbers to be assigned to ov511 devices.
+	For example, "unit_video=1,3" will make the driver use /dev/video1 and
+	/dev/video3 for the first two devices it detects. Additional devices
+	will be assigned automatically starting at the first available device
+	node (/dev/video0 in this case). Note that you cannot specify 0 as a
+	minor number. This feature requires kernel version 2.4.5 or higher.
+
+  NAME: remove_zeros
+  TYPE: integer (Boolean)
+  DEFAULT: 0 (do not skip any incoming data)
+  DESC: Setting this to 1 will remove zero-padding from incoming data. This
+	will compensate for the blocks of corruption that can appear when the
+	camera cannot keep up with the speed of the USB bus (eg. at low frame
+	resolutions). This feature is always enabled when compression is on.
+ 
 WORKING FEATURES:
- o Color streaming/capture at 640x480, 448x336, 384x288, 352x288, and 320x240
- o RGB24, RGB565, YUV420, YUV422, YUYV, and YUV422P color
- o Monochrome
+ o Color streaming/capture at most widths and heights that are multiples of 8.
+ o RGB24, RGB565, YUV420/YUV420P, YUV422/YUYV, and YUV422P color
+ o Monochrome (use force_palette=1 to enable)
  o Setting/getting of saturation, contrast, brightness, and hue (only some of
    them work the OV7620 and OV7620AE)
  o /proc status reporting
+ o SAA7111A video capture support at 320x240 and 640x480
+ o Compression support
 
 EXPERIMENTAL FEATURES:
- o fix_rgb_offset: Sometimes works, but other times causes errors with xawtv and
-   corrupted frames. If you have a very fast CPU, you can try it.
- o Snapshot mode (only works with some read() based apps; see below for more)
- o OV6620 sensor support
- o GBR422 parsing
- o 160x120
-
-TODO:
- o Fix the noise / grainy image problem.
- o Get compression working. It would be a nice addition as it improves
-   frame rate quite a bit. OmniVision wouldn't tell me how the algorithm works,
-   so we can't really work on that yet. Please kindly inform OmniVision that you
-   would like them to release their specifications to the Linux community.
- o YUV422
- o Fix fixFrameRGBoffset(). It is not stable yet with streaming video.
- o V4L2 support (Probably not until it goes into the kernel)
- o Get rid of the memory management functions (put them in videodev.c??)
- o Setting of contrast and brightness not working with 7620/7620AE
- o Driver/camera state save/restore for when USB supports suspend/resume
- o Unstable on SMP systems
- o OV7620/OV6620 experience frame corruption with moving objects
- o OV6620 is too dark
- o 176x144 support
- o Driver sometimes hangs upon close() with OHCI
- o The image should always be written properly to the mmap'ed buffer as long as
-   the requested image size is at least the minimum size. This will likely
-   require a rewrite of all the parsing code.
+ o OV6630 sensor support
+ o Banding filter
+ o SMP compatibility
+
+TO-DO:
+ o V4L2 support (This will be done after the next kernel patch release)
+ o Setting of hue not working with OV7620
+ o Setting of contrast and hue not working with OV7620AE
+ o OV8600 sensor support (Not used in anything yet)
+ o OV518/OV518+ support (all that's needed is the decompressor)
+ o cams >= 3 not working
 
 HOW TO CONTACT ME:
 
-You can email me at mwm@i.am . Please prefix the subject line
+You can email me at mmcclell@bigfoot.com . Please prefix the subject line
 with "OV511: " so that I am certain to notice your message.
 
 CREDITS:
@@ -232,3 +329,4 @@
 and the USB stack. Thanks to Bret Wallach for getting camera reg IO, ISOC, and
 image capture working. Thanks to Orion Sky Lawlor, Kevin Moore, and Claudio
 Matsuoka for their work as well.
+
diff -urN linux-2.5.2-pre7/Makefile linux/Makefile
--- linux-2.5.2-pre7/Makefile	Fri Jan  4 19:01:16 2002
+++ linux/Makefile	Fri Jan  4 19:01:19 2002
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 5
 SUBLEVEL = 2
-EXTRAVERSION =-pre7
+EXTRAVERSION =-pre8
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
diff -urN linux-2.5.2-pre7/arch/alpha/kernel/pci-noop.c linux/arch/alpha/kernel/pci-noop.c
--- linux-2.5.2-pre7/arch/alpha/kernel/pci-noop.c	Fri Jan  4 19:01:16 2002
+++ linux/arch/alpha/kernel/pci-noop.c	Fri Jan  4 19:01:19 2002
@@ -126,6 +126,7 @@
 pci_map_sg(struct pci_dev *pdev, struct scatterlist *sg, int nents,
 	   int direction)
 {
+	return 0;
 }
 void
 pci_unmap_sg(struct pci_dev *pdev, struct scatterlist *sg, int nents,
diff -urN linux-2.5.2-pre7/arch/alpha/kernel/pci_iommu.c linux/arch/alpha/kernel/pci_iommu.c
--- linux-2.5.2-pre7/arch/alpha/kernel/pci_iommu.c	Mon Nov  5 09:47:41 2001
+++ linux/arch/alpha/kernel/pci_iommu.c	Fri Jan  4 19:01:19 2002
@@ -215,12 +215,20 @@
 	/* If the machine doesn't define a pci_tbi routine, we have to
 	   assume it doesn't support sg mapping.  */
 	if (! alpha_mv.mv_pci_tbi) {
-		printk(KERN_WARNING "pci_map_single failed: no hw sg\n");
-		return 0;
+		static int been_here = 0;
+		if (!been_here) {
+		    printk(KERN_WARNING "pci_map_single: no hw sg, using "
+			   "direct map when possible\n");
+		    been_here = 1;
+		}
+		if (paddr + size <= __direct_map_size)
+		    return (paddr + __direct_map_base);
+		else
+		    return 0;
 	}
 		
 	arena = hose->sg_pci;
-	if (!arena || arena->dma_base + arena->size > max_dma)
+	if (!arena || arena->dma_base + arena->size - 1 > max_dma)
 		arena = hose->sg_isa;
 
 	npages = calc_npages((paddr & ~PAGE_MASK) + size);
@@ -247,20 +255,27 @@
 dma_addr_t
 pci_map_single(struct pci_dev *pdev, void *cpu_addr, size_t size, int dir)
 {
+	int dac_allowed; 
+
 	if (dir == PCI_DMA_NONE)
 		BUG();
-	return pci_map_single_1(pdev, cpu_addr, size,
-				pdev ? (pdev->dma_mask >> 32) != 0 : 0);
+
+	dac_allowed = pdev ? pci_dac_dma_supported(pdev, pdev->dma_mask) : 0; 
+	return pci_map_single_1(pdev, cpu_addr, size, dac_allowed);
 }
 
 dma_addr_t
 pci_map_page(struct pci_dev *pdev, struct page *page, unsigned long offset,
 	     size_t size, int dir)
 {
+	int dac_allowed;
+
 	if (dir == PCI_DMA_NONE)
 		BUG();
-	return pci_map_single_1(pdev, (char *)page_address(page) + offset,
-			        size, pdev ? (pdev->dma_mask >> 32) != 0 : 0);
+
+	dac_allowed = pdev ? pci_dac_dma_supported(pdev, pdev->dma_mask) : 0; 
+	return pci_map_single_1(pdev, (char *)page_address(page) + offset, 
+				size, dac_allowed);
 }
 
 /* Unmap a single streaming mode DMA translation.  The DMA_ADDR and
@@ -558,7 +573,7 @@
 	if (direction == PCI_DMA_NONE)
 		BUG();
 
-	dac_allowed = ((pdev->dma_mask >> 32) != 0);
+	dac_allowed = pdev ? pci_dac_dma_supported(pdev, pdev->dma_mask) : 0;
 
 	/* Fast path single entry scatterlists.  */
 	if (nents == 1) {
@@ -580,7 +595,7 @@
 		hose = pdev ? pdev->sysdata : pci_isa_hose;
 		max_dma = pdev ? pdev->dma_mask : 0x00ffffff;
 		arena = hose->sg_pci;
-		if (!arena || arena->dma_base + arena->size > max_dma)
+		if (!arena || arena->dma_base + arena->size - 1 > max_dma)
 			arena = hose->sg_isa;
 	} else {
 		max_dma = -1;
@@ -643,7 +658,7 @@
 	hose = pdev ? pdev->sysdata : pci_isa_hose;
 	max_dma = pdev ? pdev->dma_mask : 0x00ffffff;
 	arena = hose->sg_pci;
-	if (!arena || arena->dma_base + arena->size > max_dma)
+	if (!arena || arena->dma_base + arena->size - 1 > max_dma)
 		arena = hose->sg_isa;
 
 	fbeg = -1, fend = 0;
@@ -710,11 +725,10 @@
 	struct pci_iommu_arena *arena;
 
 	/* If there exists a direct map, and the mask fits either
-	   MAX_DMA_ADDRESS defined such that GFP_DMA does something
-	   useful, or the total system memory as shifted by the
-	   map base.  */
+	   the entire direct mapped space or the total system memory as
+	   shifted by the map base */
 	if (__direct_map_size != 0
-	    && (__direct_map_base + MAX_DMA_ADDRESS-IDENT_ADDR-1 <= mask
+	    && (__direct_map_base + __direct_map_size - 1 <= mask
 		|| __direct_map_base + (max_low_pfn<<PAGE_SHIFT)-1 <= mask))
 		return 1;
 
diff -urN linux-2.5.2-pre7/arch/alpha/lib/dec_and_lock.c linux/arch/alpha/lib/dec_and_lock.c
--- linux-2.5.2-pre7/arch/alpha/lib/dec_and_lock.c	Fri Nov  9 13:39:57 2001
+++ linux/arch/alpha/lib/dec_and_lock.c	Fri Jan  4 19:01:19 2002
@@ -27,6 +27,7 @@
 	br	$atomic_dec_and_lock_1..ng	\n\
 	.subsection 2				\n\
 4:	br	1b				\n\
+	.previous				\n\
 	.end atomic_dec_and_lock");
 
 static int __attribute__((unused))
diff -urN linux-2.5.2-pre7/arch/i386/defconfig linux/arch/i386/defconfig
--- linux-2.5.2-pre7/arch/i386/defconfig	Fri Jan  4 19:01:16 2002
+++ linux/arch/i386/defconfig	Fri Jan  4 19:01:19 2002
@@ -728,8 +728,9 @@
 # CONFIG_USB_LONG_TIMEOUT is not set
 
 #
-# USB Controllers
+# USB Host Controller Drivers
 #
+# CONFIG_USB_EHCI_HCD is not set
 CONFIG_USB_UHCI_ALT=y
 # CONFIG_USB_OHCI is not set
 
@@ -800,6 +801,7 @@
 # CONFIG_USB_SERIAL_EMPEG is not set
 # CONFIG_USB_SERIAL_FTDI_SIO is not set
 # CONFIG_USB_SERIAL_VISOR is not set
+# CONFIG_USB_SERIAL_IPAQ is not set
 # CONFIG_USB_SERIAL_IR is not set
 # CONFIG_USB_SERIAL_EDGEPORT is not set
 # CONFIG_USB_SERIAL_KEYSPAN_PDA is not set
@@ -813,6 +815,7 @@
 # CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set
 # CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set
 # CONFIG_USB_SERIAL_MCT_U232 is not set
+# CONFIG_USB_SERIAL_KLSI is not set
 # CONFIG_USB_SERIAL_PL2303 is not set
 # CONFIG_USB_SERIAL_CYBERJACK is not set
 # CONFIG_USB_SERIAL_XIRCOM is not set
diff -urN linux-2.5.2-pre7/drivers/block/nbd.c linux/drivers/block/nbd.c
--- linux-2.5.2-pre7/drivers/block/nbd.c	Fri Jan  4 19:01:16 2002
+++ linux/drivers/block/nbd.c	Fri Jan  4 19:01:20 2002
@@ -78,7 +78,7 @@
 
 	if (!inode)
 		return -EINVAL;
-	dev = MINOR(inode->i_rdev);
+	dev = minor(inode->i_rdev);
 	if (dev >= MAX_NBD)
 		return -ENODEV;
 
@@ -253,7 +253,7 @@
 		if (req != blkdev_entry_prev_request(&lo->queue_head)) {
 			printk(KERN_ALERT "NBD: I have problem...\n");
 		}
-		if (lo != &nbd_dev[MINOR(req->rq_dev)]) {
+		if (lo != &nbd_dev[minor(req->rq_dev)]) {
 			printk(KERN_ALERT "NBD: request corrupted!\n");
 			continue;
 		}
@@ -291,7 +291,7 @@
 			printk( KERN_ALERT "NBD: panic, panic, panic\n" );
 			break;
 		}
-		if (lo != &nbd_dev[MINOR(req->rq_dev)]) {
+		if (lo != &nbd_dev[minor(req->rq_dev)]) {
 			printk(KERN_ALERT "NBD: request corrupted when clearing!\n");
 			continue;
 		}
@@ -328,7 +328,7 @@
 		if (!req)
 			FAIL("que not empty but no request?");
 #endif
-		dev = MINOR(req->rq_dev);
+		dev = minor(req->rq_dev);
 #ifdef PARANOIA
 		if (dev >= MAX_NBD)
 			FAIL("Minor too big.");		/* Probably can not happen */
@@ -381,7 +381,7 @@
 		return -EPERM;
 	if (!inode)
 		return -EINVAL;
-	dev = MINOR(inode->i_rdev);
+	dev = minor(inode->i_rdev);
 	if (dev >= MAX_NBD)
 		return -ENODEV;
 
@@ -473,7 +473,7 @@
 
 	if (!inode)
 		return -ENODEV;
-	dev = MINOR(inode->i_rdev);
+	dev = minor(inode->i_rdev);
 	if (dev >= MAX_NBD)
 		return -ENODEV;
 	lo = &nbd_dev[dev];
@@ -528,7 +528,7 @@
 		nbd_blksize_bits[i] = 10;
 		nbd_bytesizes[i] = 0x7ffffc00; /* 2GB */
 		nbd_sizes[i] = nbd_bytesizes[i] >> BLOCK_SIZE_BITS;
-		register_disk(NULL, MKDEV(MAJOR_NR,i), 1, &nbd_fops,
+		register_disk(NULL, mk_kdev(MAJOR_NR,i), 1, &nbd_fops,
 				nbd_bytesizes[i]>>9);
 	}
 	devfs_handle = devfs_mk_dir (NULL, "nbd", NULL);
diff -urN linux-2.5.2-pre7/drivers/block/paride/pcd.c linux/drivers/block/paride/pcd.c
--- linux-2.5.2-pre7/drivers/block/paride/pcd.c	Wed Dec 12 13:51:57 2001
+++ linux/drivers/block/paride/pcd.c	Fri Jan  4 19:01:20 2002
@@ -182,7 +182,7 @@
 #define MAJOR_NR	major
 #define DEVICE_NAME "PCD"
 #define DEVICE_REQUEST do_pcd_request
-#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_NR(device) (minor(device))
 #define DEVICE_ON(device)
 #define DEVICE_OFF(device)
 
@@ -325,7 +325,7 @@
 
 		PCD.info.ops = &pcd_dops;
 		PCD.info.handle = NULL;
-		PCD.info.dev = MKDEV(major,unit);
+		PCD.info.dev = mk_kdev(major,unit);
 		PCD.info.speed = 0;
 		PCD.info.capacity = 1;
 		PCD.info.mask = 0;
@@ -771,7 +771,7 @@
 	    if (QUEUE_EMPTY || (CURRENT->rq_status == RQ_INACTIVE)) return;
 	    INIT_REQUEST;
 	    if (rq_data_dir(CURRENT) == READ) {
-		unit = MINOR(CURRENT->rq_dev);
+		unit = minor(CURRENT->rq_dev);
 		if (unit != pcd_unit) {
 			pcd_bufblk = -1;
 			pcd_unit = unit;
diff -urN linux-2.5.2-pre7/drivers/block/paride/pd.c linux/drivers/block/paride/pd.c
--- linux-2.5.2-pre7/drivers/block/paride/pd.c	Fri Jan  4 19:01:16 2002
+++ linux/drivers/block/paride/pd.c	Fri Jan  4 19:01:20 2002
@@ -206,7 +206,7 @@
 #define MAJOR_NR   major
 #define DEVICE_NAME "PD"
 #define DEVICE_REQUEST do_pd_request
-#define DEVICE_NR(device) (MINOR(device)>>PD_BITS)
+#define DEVICE_NR(device) (minor(device)>>PD_BITS)
 #define DEVICE_ON(device)
 #define DEVICE_OFF(device)
 
@@ -445,7 +445,7 @@
 	struct hd_geometry *geo = (struct hd_geometry *) arg;
 	int err, unit;
 
-	if (!inode || !inode->i_rdev)
+	if (!inode || kdev_none(inode->i_rdev))
 		return -EINVAL;
 	unit = DEVICE_NR(inode->i_rdev);
 	if (!PD.present)
@@ -814,7 +814,7 @@
                 } else pi_release(PI);
             }
 	for (unit=0;unit<PD_UNITS;unit++)
-		register_disk(&pd_gendisk,MKDEV(MAJOR_NR,unit<<PD_BITS),
+		register_disk(&pd_gendisk,mk_kdev(MAJOR_NR,unit<<PD_BITS),
 				PD_PARTNS,&pd_fops,
 				PD.present?PD.capacity:0);
 
@@ -847,7 +847,7 @@
         if (QUEUE_EMPTY || (CURRENT->rq_status == RQ_INACTIVE)) return;
         INIT_REQUEST;
 
-        pd_dev = MINOR(CURRENT->rq_dev);
+        pd_dev = minor(CURRENT->rq_dev);
 	pd_unit = unit = DEVICE_NR(CURRENT->rq_dev);
         pd_block = CURRENT->sector;
         pd_run = CURRENT->nr_sectors;
@@ -886,7 +886,7 @@
 
 	if (QUEUE_EMPTY ||
 	    (rq_data_dir(CURRENT) != pd_cmd) ||
-	    (MINOR(CURRENT->rq_dev) != pd_dev) ||
+	    (minor(CURRENT->rq_dev) != pd_dev) ||
 	    (CURRENT->rq_status == RQ_INACTIVE) ||
 	    (CURRENT->sector != pd_block)) 
 		printk("%s: OUCH: request list changed unexpectedly\n",
diff -urN linux-2.5.2-pre7/drivers/block/paride/pf.c linux/drivers/block/paride/pf.c
--- linux-2.5.2-pre7/drivers/block/paride/pf.c	Sun Dec 16 12:20:20 2001
+++ linux/drivers/block/paride/pf.c	Fri Jan  4 19:01:20 2002
@@ -202,7 +202,7 @@
 #define MAJOR_NR   major
 #define DEVICE_NAME "PF"
 #define DEVICE_REQUEST do_pf_request
-#define DEVICE_NR(device) MINOR(device)
+#define DEVICE_NR(device) minor(device)
 #define DEVICE_ON(device)
 #define DEVICE_OFF(device)
 
@@ -368,7 +368,7 @@
 	for (i=0;i<PF_UNITS;i++) pf_blocksizes[i] = 1024;
 	blksize_size[MAJOR_NR] = pf_blocksizes;
 	for (i=0;i<PF_UNITS;i++)
-		register_disk(NULL, MKDEV(MAJOR_NR, i), 1, &pf_fops, 0);
+		register_disk(NULL, mk_kdev(MAJOR_NR, i), 1, &pf_fops, 0);
 
         return 0;
 }
@@ -399,7 +399,7 @@
 {       int err, unit;
 	struct hd_geometry *geo = (struct hd_geometry *) arg;
 
-        if ((!inode) || (!inode->i_rdev)) return -EINVAL;
+        if ((!inode) || kdev_none(inode->i_rdev)) return -EINVAL;
         unit = DEVICE_NR(inode->i_rdev);
         if (unit >= PF_UNITS) return -EINVAL;
         if (!PF.present) return -ENODEV;
diff -urN linux-2.5.2-pre7/drivers/block/paride/pg.c linux/drivers/block/paride/pg.c
--- linux-2.5.2-pre7/drivers/block/paride/pg.c	Fri Nov 30 08:26:04 2001
+++ linux/drivers/block/paride/pg.c	Fri Jan  4 19:01:20 2002
@@ -565,7 +565,7 @@
 	return -1;
 }
 
-#define DEVICE_NR(dev)	(MINOR(dev) % 128)
+#define DEVICE_NR(dev)	(minor(dev) & 0x7F)
 
 static int pg_open (struct inode *inode, struct file *file)
 
diff -urN linux-2.5.2-pre7/drivers/block/paride/pt.c linux/drivers/block/paride/pt.c
--- linux-2.5.2-pre7/drivers/block/paride/pt.c	Fri Nov 30 08:26:04 2001
+++ linux/drivers/block/paride/pt.c	Fri Jan  4 19:01:20 2002
@@ -688,7 +688,7 @@
 	return -1;
 }
 
-#define DEVICE_NR(dev)	(MINOR(dev) % 128)
+#define DEVICE_NR(dev)	(minor(dev) & 0x7F)
 
 static int pt_open (struct inode *inode, struct file *file)
 
@@ -713,7 +713,7 @@
 		return -EROFS;
 		}
 
-	if (!(MINOR(inode->i_rdev) & 128))
+	if (!(minor(inode->i_rdev) & 128))
 		PT.flags |= PT_REWIND;
 
 	PT.bufptr = kmalloc(PT_BUFSIZE,GFP_KERNEL);
@@ -732,7 +732,7 @@
 	int unit;
 	struct mtop mtop;
 
-        if (!inode || !inode->i_rdev)
+        if (!inode || kdev_none(inode->i_rdev))
 		return -EINVAL;
         unit = DEVICE_NR(inode->i_rdev);
         if (unit >= PT_UNITS)
diff -urN linux-2.5.2-pre7/drivers/block/rd.c linux/drivers/block/rd.c
--- linux-2.5.2-pre7/drivers/block/rd.c	Fri Jan  4 19:01:16 2002
+++ linux/drivers/block/rd.c	Fri Jan  4 19:01:20 2002
@@ -453,7 +453,7 @@
 
 #ifdef CONFIG_BLK_DEV_INITRD
 	/* We ought to separate initrd operations here */
-	register_disk(NULL, MKDEV(MAJOR_NR,INITRD_MINOR), 1, &rd_bd_op, rd_size<<1);
+	register_disk(NULL, mk_kdev(MAJOR_NR,INITRD_MINOR), 1, &rd_bd_op, rd_size<<1);
 	devfs_register(devfs_handle, "initrd", DEVFS_FL_DEFAULT, MAJOR_NR,
 			INITRD_MINOR, S_IFBLK | S_IRUSR, &rd_bd_op, NULL);
 #endif
diff -urN linux-2.5.2-pre7/drivers/block/xd.c linux/drivers/block/xd.c
--- linux-2.5.2-pre7/drivers/block/xd.c	Fri Jan  4 19:01:16 2002
+++ linux/drivers/block/xd.c	Fri Jan  4 19:01:20 2002
@@ -249,7 +249,8 @@
 
 	for (i = 0; i < xd_drives; i++) {
 		xd_valid[i] = 1;
-		register_disk(&xd_gendisk, MKDEV(MAJOR_NR,i<<6), 1<<6, &xd_fops,
+		register_disk(&xd_gendisk, mk_kdev(MAJOR_NR,i<<6), 1<<6,
+				&xd_fops,
 				xd_info[i].heads * xd_info[i].cylinders *
 				xd_info[i].sectors);
 	}
@@ -287,8 +288,9 @@
 		INIT_REQUEST;	/* do some checking on the request structure */
 
 		if (CURRENT_DEV < xd_drives
+		    && (CURRENT->flags & REQ_CMD)
 		    && CURRENT->sector + CURRENT->nr_sectors
-		         <= xd_struct[MINOR(CURRENT->rq_dev)].nr_sects) {
+		         <= xd_struct[minor(CURRENT->rq_dev)].nr_sects) {
 			block = CURRENT->sector;
 			count = CURRENT->nr_sectors;
 
@@ -312,7 +314,7 @@
 {
 	int dev;
 
-	if ((!inode) || !(inode->i_rdev))
+	if ((!inode) || kdev_none(inode->i_rdev))
 		return -EINVAL;
  	dev = DEVICE_NR(inode->i_rdev);
 
diff -urN linux-2.5.2-pre7/drivers/cdrom/aztcd.c linux/drivers/cdrom/aztcd.c
--- linux-2.5.2-pre7/drivers/cdrom/aztcd.c	Thu Oct 25 13:58:35 2001
+++ linux/drivers/cdrom/aztcd.c	Fri Jan  4 19:01:20 2002
@@ -229,7 +229,7 @@
 #endif
 
 #define CURRENT_VALID \
-  (!QUEUE_EMPTY && MAJOR(CURRENT -> rq_dev) == MAJOR_NR && CURRENT -> cmd == READ \
+  (!QUEUE_EMPTY && major(CURRENT -> rq_dev) == MAJOR_NR && CURRENT -> cmd == READ \
    && CURRENT -> sector != -1)
 
 #define AFL_STATUSorDATA (AFL_STATUS | AFL_DATA)
@@ -309,6 +309,8 @@
 static unsigned long aztTimeOutCount;
 static int aztCmd = 0;
 
+static spinlock_t aztSpin = SPIN_LOCK_UNLOCKED;
+
 /*###########################################################################
    Function Prototypes
   ###########################################################################
@@ -1599,10 +1601,6 @@
 	}
 	azt_transfer_is_active = 1;
 	while (CURRENT_VALID) {
-		if (CURRENT->bh) {
-			if (!buffer_locked(CURRENT->bh))
-				panic(DEVICE_NAME ": block not locked");
-		}
 		azt_transfer();
 		if (CURRENT->nr_sectors == 0) {
 			end_request(1);
@@ -1927,10 +1925,10 @@
 		       MAJOR_NR);
 		return -EIO;
 	}
-	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST, &aztSpin);
 	blksize_size[MAJOR_NR] = aztcd_blocksizes;
 	read_ahead[MAJOR_NR] = 4;
-	register_disk(NULL, MKDEV(MAJOR_NR, 0), 1, &azt_fops, 0);
+	register_disk(NULL, mk_kdev(MAJOR_NR, 0), 1, &azt_fops, 0);
 
 	if ((azt_port == 0x1f0) || (azt_port == 0x170))
 		request_region(azt_port, 8, "aztcd");	/*IDE-interface */
diff -urN linux-2.5.2-pre7/drivers/cdrom/cdu31a.c linux/drivers/cdrom/cdu31a.c
--- linux-2.5.2-pre7/drivers/cdrom/cdu31a.c	Fri Jan  4 19:01:16 2002
+++ linux/drivers/cdrom/cdu31a.c	Fri Jan  4 19:01:20 2002
@@ -1587,7 +1587,7 @@
 	/*
 	 * jens: driver has lots of races
 	 */
-	spin_unlock_irq(&q->queue_lock);
+	spin_unlock_irq(q->queue_lock);
 
 	/* Make sure the timer is cancelled. */
 	del_timer(&cdu31a_abort_timer);
@@ -1717,7 +1717,7 @@
 		}
 	}
       end_do_cdu31a_request:
-	spin_lock_irq(&q->queue_lock);
+	spin_lock_irq(q->queue_lock);
 #if 0
 	/* After finished, cancel any pending operations. */
 	abort_read();
@@ -3450,7 +3450,7 @@
 		init_timer(&cdu31a_abort_timer);
 		cdu31a_abort_timer.function = handle_abort_timeout;
 
-		scd_info.dev = MKDEV(MAJOR_NR, 0);
+		scd_info.dev = mk_kdev(MAJOR_NR, 0);
 		scd_info.mask = deficiency;
 		strncpy(scd_info.name, "cdu31a", sizeof(scd_info.name));
 
diff -urN linux-2.5.2-pre7/drivers/cdrom/cm206.c linux/drivers/cdrom/cm206.c
--- linux-2.5.2-pre7/drivers/cdrom/cm206.c	Tue Nov 27 09:23:27 2001
+++ linux/drivers/cdrom/cm206.c	Fri Jan  4 19:01:20 2002
@@ -302,6 +302,7 @@
 #define PLAY_TO cd->toc[0]	/* toc[0] records end-time in play */
 
 static struct cm206_struct *cd;	/* the main memory structure */
+static spinlock_t cm206_lock = SPIN_LOCK_UNLOCKED;
 
 /* First, we define some polling functions. These are actually
    only being used in the initialization. */
@@ -866,7 +867,7 @@
 			end_request(0);
 			continue;
 		}
-		spin_unlock_irq(&q->queue_lock);
+		spin_unlock_irq(q->queue_lock);
 		error = 0;
 		for (i = 0; i < CURRENT->nr_sectors; i++) {
 			int e1, e2;
@@ -893,7 +894,7 @@
 				debug(("cm206_request: %d %d\n", e1, e2));
 			}
 		}
-		spin_lock_irq(&q->queue_lock);
+		spin_lock_irq(q->queue_lock);
 		end_request(!error);
 	}
 }
@@ -1491,7 +1492,7 @@
 		cleanup(3);
 		return -EIO;
 	}
-	cm206_info.dev = MKDEV(MAJOR_NR, 0);
+	cm206_info.dev = mk_kdev(MAJOR_NR, 0);
 	if (register_cdrom(&cm206_info) != 0) {
 		printk(KERN_INFO "Cannot register for cdrom %d!\n",
 		       MAJOR_NR);
@@ -1499,7 +1500,8 @@
 		return -EIO;
 	}
 	devfs_plain_cdrom(&cm206_info, &cm206_bdops);
-	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST,
+		       &cm206_lock);
 	blksize_size[MAJOR_NR] = cm206_blocksizes;
 	read_ahead[MAJOR_NR] = 16;	/* reads ahead what? */
 	init_bh(CM206_BH, cm206_bh);
diff -urN linux-2.5.2-pre7/drivers/cdrom/gscd.c linux/drivers/cdrom/gscd.c
--- linux-2.5.2-pre7/drivers/cdrom/gscd.c	Thu Oct 25 13:58:35 2001
+++ linux/drivers/cdrom/gscd.c	Fri Jan  4 19:01:20 2002
@@ -162,6 +162,7 @@
 static int AudioEnd_f;
 
 static struct timer_list gscd_timer;
+static spinlock_t gscd_lock = SPIN_LOCK_UNLOCKED;
 
 static struct block_device_operations gscd_fops = {
 	owner:THIS_MODULE,
@@ -180,7 +181,7 @@
 	int target;
 
 
-	target = MINOR(full_dev);
+	target = minor(full_dev);
 
 	if (target > 0) {
 		printk
@@ -283,7 +284,7 @@
 	if (QUEUE_EMPTY || CURRENT->rq_status == RQ_INACTIVE)
 		goto out;
 	INIT_REQUEST;
-	dev = MINOR(CURRENT->rq_dev);
+	dev = minor(CURRENT->rq_dev);
 	block = CURRENT->sector;
 	nsect = CURRENT->nr_sectors;
 
@@ -296,7 +297,7 @@
 		goto repeat;
 	}
 
-	if (MINOR(CURRENT->rq_dev) != 0) {
+	if (dev != 0) {
 		printk("GSCD: this version supports only one device\n");
 		end_request(0);
 		goto repeat;
@@ -1019,7 +1020,7 @@
 	devfs_register(NULL, "gscd", DEVFS_FL_DEFAULT, MAJOR_NR, 0,
 		       S_IFBLK | S_IRUGO | S_IWUGO, &gscd_fops, NULL);
 
-	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST, &gscd_lock);
 	blksize_size[MAJOR_NR] = gscd_blocksizes;
 	read_ahead[MAJOR_NR] = 4;
 
@@ -1027,7 +1028,7 @@
 	gscdPresent = 1;
 
 	request_region(gscd_port, 4, "gscd");
-	register_disk(NULL, MKDEV(MAJOR_NR, 0), 1, &gscd_fops, 0);
+	register_disk(NULL, mk_kdev(MAJOR_NR, 0), 1, &gscd_fops, 0);
 
 	printk(KERN_INFO "GSCD: GoldStar CD-ROM Drive found.\n");
 	return 0;
diff -urN linux-2.5.2-pre7/drivers/cdrom/mcd.c linux/drivers/cdrom/mcd.c
--- linux-2.5.2-pre7/drivers/cdrom/mcd.c	Thu Oct 25 13:58:35 2001
+++ linux/drivers/cdrom/mcd.c	Fri Jan  4 19:01:20 2002
@@ -123,7 +123,7 @@
 #define QUICK_LOOP_COUNT 20
 
 #define CURRENT_VALID \
-(!QUEUE_EMPTY && MAJOR(CURRENT -> rq_dev) == MAJOR_NR && CURRENT -> cmd == READ \
+(!QUEUE_EMPTY && major(CURRENT -> rq_dev) == MAJOR_NR && CURRENT -> cmd == READ \
 && CURRENT -> sector != -1)
 
 #define MFL_STATUSorDATA (MFL_STATUS | MFL_DATA)
@@ -185,6 +185,7 @@
 static void mcd_release(struct cdrom_device_info *cdi);
 static int mcd_media_changed(struct cdrom_device_info *cdi, int disc_nr);
 static int mcd_tray_move(struct cdrom_device_info *cdi, int position);
+static spinlock_t mcd_spinlock = SPIN_LOCK_UNLOCKED;
 int mcd_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
 		    void *arg);
 int mcd_drive_status(struct cdrom_device_info *cdi, int slot_nr);
@@ -617,10 +618,6 @@
 
 		mcd_transfer_is_active = 1;
 	while (CURRENT_VALID) {
-		if (CURRENT->bh) {
-			if (!buffer_locked(CURRENT->bh))
-				panic(DEVICE_NAME ": block not locked");
-		}
 		mcd_transfer();
 		if (CURRENT->nr_sectors == 0) {
 			end_request(1);
@@ -1076,7 +1073,8 @@
 	}
 
 	blksize_size[MAJOR_NR] = mcd_blocksizes;
-	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST,
+		       &mcd_spinlock);
 	read_ahead[MAJOR_NR] = 4;
 
 	/* check for card */
@@ -1150,7 +1148,7 @@
 	mcd_invalidate_buffers();
 	mcdPresent = 1;
 
-	mcd_info.dev = MKDEV(MAJOR_NR, 0);
+	mcd_info.dev = mk_kdev(MAJOR_NR, 0);
 
 	if (register_cdrom(&mcd_info) != 0) {
 		printk(KERN_ERR "mcd: Unable to register Mitsumi CD-ROM.\n");
diff -urN linux-2.5.2-pre7/drivers/cdrom/mcdx.c linux/drivers/cdrom/mcdx.c
--- linux-2.5.2-pre7/drivers/cdrom/mcdx.c	Thu Oct 25 13:58:35 2001
+++ linux/drivers/cdrom/mcdx.c	Fri Jan  4 19:01:20 2002
@@ -291,6 +291,7 @@
 static struct s_drive_stuff *mcdx_irq_map[16] = { 0, 0, 0, 0, 0, 0, 0, 0,
 	0, 0, 0, 0, 0, 0, 0, 0
 };
+static spinlock_t mcdx_lock = SPIN_LOCK_UNLOCKED;
 MODULE_PARM(mcdx, "1-4i");
 
 static struct cdrom_device_ops mcdx_dops = {
@@ -318,7 +319,7 @@
 static int mcdx_audio_ioctl(struct cdrom_device_info *cdi,
 			    unsigned int cmd, void *arg)
 {
-	struct s_drive_stuff *stuffp = mcdx_stuffp[MINOR(cdi->dev)];
+	struct s_drive_stuff *stuffp = mcdx_stuffp[minor(cdi->dev)];
 
 	if (!stuffp->present)
 		return -ENXIO;
@@ -575,7 +576,7 @@
 
 	INIT_REQUEST;
 
-	dev = MINOR(CURRENT->rq_dev);
+	dev = minor(CURRENT->rq_dev);
 	stuffp = mcdx_stuffp[dev];
 
 	if ((dev < 0)
@@ -598,14 +599,13 @@
 	xtrace(REQUEST, "do_request() (%lu + %lu)\n",
 	       CURRENT->sector, CURRENT->nr_sectors);
 
-	switch (CURRENT->cmd) {
-	case WRITE:
-		xwarn("do_request(): attempt to write to cd!!\n");
+	if (CURRENT->cmd != READ) {
+		xwarn("do_request(): non-read command to cd!!\n");
 		xtrace(REQUEST, "end_request(0): write\n");
 		end_request(0);
 		return;
-
-	case READ:
+	}
+	else {
 		stuffp->status = 0;
 		while (CURRENT->nr_sectors) {
 			int i;
@@ -628,11 +628,6 @@
 
 		xtrace(REQUEST, "end_request(1)\n");
 		end_request(1);
-		break;
-
-	default:
-		panic(MCDX "do_request: unknown command.\n");
-		break;
 	}
 
 	goto again;
@@ -642,7 +637,7 @@
 {
 	struct s_drive_stuff *stuffp;
 	xtrace(OPENCLOSE, "open()\n");
-	stuffp = mcdx_stuffp[MINOR(cdi->dev)];
+	stuffp = mcdx_stuffp[minor(cdi->dev)];
 	if (!stuffp->present)
 		return -ENXIO;
 
@@ -791,7 +786,7 @@
 
 	xtrace(OPENCLOSE, "close()\n");
 
-	stuffp = mcdx_stuffp[MINOR(cdi->dev)];
+	stuffp = mcdx_stuffp[minor(cdi->dev)];
 
 	--stuffp->users;
 }
@@ -805,7 +800,7 @@
 	xinfo("mcdx_media_changed called for device %s\n",
 	      kdevname(cdi->dev));
 
-	stuffp = mcdx_stuffp[MINOR(cdi->dev)];
+	stuffp = mcdx_stuffp[minor(cdi->dev)];
 	mcdx_getstatus(stuffp, 1);
 
 	if (stuffp->yyy == 0)
@@ -1187,7 +1182,8 @@
 		return 1;
 	}
 
-	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST,
+		       &mcdx_lock);
 	read_ahead[MAJOR_NR] = READ_AHEAD;
 	blksize_size[MAJOR_NR] = mcdx_blocksizes;
 
@@ -1228,7 +1224,7 @@
 		stuffp->wreg_data, stuffp->irq, version.code, version.ver);
 	mcdx_stuffp[drive] = stuffp;
 	xtrace(INIT, "init() mcdx_stuffp[%d] = %p\n", drive, stuffp);
-	mcdx_info.dev = MKDEV(MAJOR_NR, 0);
+	mcdx_info.dev = mk_kdev(MAJOR_NR, 0);
 	if (register_cdrom(&mcdx_info) != 0) {
 		printk("Cannot register Mitsumi CD-ROM!\n");
 		release_region((unsigned long) stuffp->wreg_data,
@@ -1698,7 +1694,7 @@
 
 static int mcdx_tray_move(struct cdrom_device_info *cdi, int position)
 {
-	struct s_drive_stuff *stuffp = mcdx_stuffp[MINOR(cdi->dev)];
+	struct s_drive_stuff *stuffp = mcdx_stuffp[minor(cdi->dev)];
 
 	if (!stuffp->present)
 		return -ENXIO;
@@ -1888,7 +1884,7 @@
 
 static int mcdx_lockdoor(struct cdrom_device_info *cdi, int lock)
 {
-	struct s_drive_stuff *stuffp = mcdx_stuffp[MINOR(cdi->dev)];
+	struct s_drive_stuff *stuffp = mcdx_stuffp[minor(cdi->dev)];
 	char cmd[2] = { 0xfe };
 
 	if (!(stuffp->present & DOOR))
diff -urN linux-2.5.2-pre7/drivers/cdrom/optcd.c linux/drivers/cdrom/optcd.c
--- linux-2.5.2-pre7/drivers/cdrom/optcd.c	Thu Oct 25 13:58:35 2001
+++ linux/drivers/cdrom/optcd.c	Fri Jan  4 19:01:20 2002
@@ -109,7 +109,6 @@
 #endif
 
 static int blksize = 2048;
-static int hsecsize = 2048;
 
 
 /* Drive hardware/firmware characteristics
@@ -267,6 +266,7 @@
 static DECLARE_WAIT_QUEUE_HEAD(waitq);
 static void sleep_timer(unsigned long data);
 static struct timer_list delay_timer = {function: sleep_timer};
+spinlock_t optcd_lock = SPIN_LOCK_UNLOCKED;
 
 
 /* Timer routine: wake up when desired flag goes low,
@@ -977,7 +977,7 @@
 
 
 #define CURRENT_VALID \
-	(!QUEUE_EMPTY && MAJOR(CURRENT -> rq_dev) == MAJOR_NR \
+	(!QUEUE_EMPTY && major(CURRENT -> rq_dev) == MAJOR_NR \
 	 && CURRENT -> cmd == READ && CURRENT -> sector != -1)
 
 
@@ -1371,10 +1371,6 @@
 
 	transfer_is_active = 1;
 	while (CURRENT_VALID) {
-		if (CURRENT->bh) {
-			if (!buffer_locked(CURRENT->bh))
-				panic(DEVICE_NAME ": block not locked");
-		}
 		transfer();	/* First try to transfer block from buffers */
 		if (CURRENT -> nr_sectors == 0) {
 			end_request(1);
@@ -2063,12 +2059,12 @@
 	}
 	devfs_register (NULL, "optcd", DEVFS_FL_DEFAULT, MAJOR_NR, 0,
 			S_IFBLK | S_IRUGO | S_IWUGO, &opt_fops, NULL);
-	hardsect_size[MAJOR_NR] = &hsecsize;
 	blksize_size[MAJOR_NR] = &blksize;
-	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST,
+		       &optcd_lock);
 	read_ahead[MAJOR_NR] = 4;
 	request_region(optcd_port, 4, "optcd");
-	register_disk(NULL, MKDEV(MAJOR_NR,0), 1, &opt_fops, 0);
+	register_disk(NULL, mk_kdev(MAJOR_NR,0), 1, &opt_fops, 0);
 
 	printk(KERN_INFO "optcd: DOLPHIN 8000 AT CDROM at 0x%x\n", optcd_port);
 	return 0;
diff -urN linux-2.5.2-pre7/drivers/cdrom/sjcd.c linux/drivers/cdrom/sjcd.c
--- linux-2.5.2-pre7/drivers/cdrom/sjcd.c	Thu Oct 25 13:58:35 2001
+++ linux/drivers/cdrom/sjcd.c	Fri Jan  4 19:01:20 2002
@@ -1664,7 +1664,6 @@
 };
 
 static int blksize = 2048;
-static int secsize = 2048;
 
 /*
  * Following stuff is intended for initialization of the cdrom. It
@@ -1692,7 +1691,6 @@
 	printk("SJCD: sjcd=0x%x: ", sjcd_base);
 #endif
 
-	hardsect_size[MAJOR_NR] = &secsize;
 	blksize_size[MAJOR_NR] = &blksize;
 
 	if (devfs_register_blkdev(MAJOR_NR, "sjcd", &sjcd_fops) != 0) {
diff -urN linux-2.5.2-pre7/drivers/char/serial.c linux/drivers/char/serial.c
--- linux-2.5.2-pre7/drivers/char/serial.c	Fri Jan  4 19:01:16 2002
+++ linux/drivers/char/serial.c	Fri Jan  4 19:01:20 2002
@@ -5827,7 +5827,7 @@
 
 static kdev_t serial_console_device(struct console *c)
 {
-	return MKDEV(TTY_MAJOR, 64 + c->index);
+	return mk_kdev(TTY_MAJOR, 64 + c->index);
 }
 
 /*
diff -urN linux-2.5.2-pre7/drivers/scsi/st.c linux/drivers/scsi/st.c
--- linux-2.5.2-pre7/drivers/scsi/st.c	Fri Jan  4 19:01:17 2002
+++ linux/drivers/scsi/st.c	Fri Jan  4 19:01:21 2002
@@ -133,8 +133,8 @@
 #define ST_TIMEOUT (900 * HZ)
 #define ST_LONG_TIMEOUT (14000 * HZ)
 
-#define TAPE_NR(x) (MINOR(x) & ~(128 | ST_MODE_MASK))
-#define TAPE_MODE(x) ((MINOR(x) & ST_MODE_MASK) >> ST_MODE_SHIFT)
+#define TAPE_NR(x) (minor(x) & ~(-1 << ST_MODE_SHIFT))
+#define TAPE_MODE(x) ((minor(x) & ST_MODE_MASK) >> ST_MODE_SHIFT)
 
 /* Internal ioctl to set both density (uppermost 8 bits) and blocksize (lower
    24 bits) */
@@ -878,7 +878,7 @@
 	}
 	STp->in_use = 1;
 	write_unlock_irqrestore(&st_dev_arr_lock, flags);
-	STp->rew_at_close = STp->autorew_dev = (MINOR(inode->i_rdev) & 0x80) == 0;
+	STp->rew_at_close = STp->autorew_dev = (minor(inode->i_rdev) & 0x80) == 0;
 
 	if (STp->device->host->hostt->module)
 		__MOD_INC_USE_COUNT(STp->device->host->hostt->module);
@@ -3717,7 +3717,7 @@
 		tpnt->tape_type = MT_ISSCSI2;
 
         tpnt->inited = 0;
-	tpnt->devt = MKDEV(SCSI_TAPE_MAJOR, i);
+	tpnt->devt = mk_kdev(SCSI_TAPE_MAJOR, i);
 	tpnt->dirty = 0;
 	tpnt->in_use = 0;
 	tpnt->drv_buffer = 1;	/* Try buffering if no mode sense */
diff -urN linux-2.5.2-pre7/drivers/sound/ymfpci.c linux/drivers/sound/ymfpci.c
--- linux-2.5.2-pre7/drivers/sound/ymfpci.c	Sun Nov 25 10:17:47 2001
+++ linux/drivers/sound/ymfpci.c	Fri Jan  4 19:01:21 2002
@@ -1816,7 +1816,7 @@
 	struct ymf_state *state;
 	int err;
 
-	minor = MINOR(inode->i_rdev);
+	minor = minor(inode->i_rdev);
 	if ((minor & 0x0F) == 3) {	/* /dev/dspN */
 		;
 	} else {
@@ -1929,7 +1929,7 @@
 static int ymf_open_mixdev(struct inode *inode, struct file *file)
 {
 	int i;
-	int minor = MINOR(inode->i_rdev);
+	int minor = minor(inode->i_rdev);
 	struct list_head *list;
 	ymfpci_t *unit;
 
diff -urN linux-2.5.2-pre7/drivers/usb/catc.c linux/drivers/usb/catc.c
--- linux-2.5.2-pre7/drivers/usb/catc.c	Tue Nov 13 09:19:41 2001
+++ linux/drivers/usb/catc.c	Fri Jan  4 19:01:21 2002
@@ -38,7 +38,9 @@
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 #include <linux/spinlock.h>
+#include <linux/ethtool.h>
 #include <asm/bitops.h>
+#include <asm/uaccess.h>
 
 #undef DEBUG
 
@@ -48,9 +50,10 @@
  * Version information.
  */
 
-#define DRIVER_VERSION "v2.7"
+#define DRIVER_VERSION "v2.8"
 #define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@suse.cz>"
 #define DRIVER_DESC "CATC EL1210A NetMate USB Ethernet driver"
+#define SHORT_DRIVER_DESC "EL1210A NetMate USB Ethernet"
 
 MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_DESCRIPTION(DRIVER_DESC);
@@ -259,11 +262,15 @@
 		} 
 	}
 
-	if (data[1] & 0x40)
+	if (data[1] & 0x40) {
+		netif_carrier_on(catc->netdev);
 		dbg("link ok");
+	}
 
-	if (data[1] & 0x20) 
+	if (data[1] & 0x20) {
+		netif_carrier_off(catc->netdev);
 		dbg("link bad");
+	}
 }
 
 /*
@@ -564,6 +571,54 @@
 }
 
 /*
+ * ioctl's
+ */
+static int netdev_ethtool_ioctl(struct net_device *dev, void *useraddr)
+{
+        struct catc *catc = dev->priv;
+        u32 cmd;
+	char tmp[40];
+        
+        if (get_user(cmd, (u32 *)useraddr))
+                return -EFAULT;
+
+        switch (cmd) {
+        /* get driver info */
+        case ETHTOOL_GDRVINFO: {
+                struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO};
+                strncpy(info.driver, SHORT_DRIVER_DESC, ETHTOOL_BUSINFO_LEN);
+                strncpy(info.version, DRIVER_VERSION, ETHTOOL_BUSINFO_LEN);
+		sprintf(tmp, "usb%d:%d", catc->usbdev->bus->busnum, catc->usbdev->devnum);
+                strncpy(info.bus_info, tmp,ETHTOOL_BUSINFO_LEN);
+                if (copy_to_user(useraddr, &info, sizeof(info)))
+                        return -EFAULT;
+                return 0;
+        }
+        /* get link status */
+        case ETHTOOL_GLINK: {
+                struct ethtool_value edata = {ETHTOOL_GLINK};
+                edata.data = netif_carrier_ok(dev);
+                if (copy_to_user(useraddr, &edata, sizeof(edata)))
+                        return -EFAULT;
+                return 0;
+        }
+	}
+        
+        return -EOPNOTSUPP;
+}
+
+static int catc_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+        switch(cmd) {
+        case SIOCETHTOOL:
+                return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+        default:
+                return -EOPNOTSUPP;
+        }
+}
+
+
+/*
  * Open, close.
  */
 
@@ -629,6 +684,7 @@
 	netdev->tx_timeout = catc_tx_timeout;
 	netdev->watchdog_timeo = TX_TIMEOUT;
 	netdev->set_multicast_list = catc_set_multicast_list;
+	netdev->do_ioctl = catc_ioctl;
 	netdev->priv = catc;
 
 	catc->usbdev = usbdev;
diff -urN linux-2.5.2-pre7/drivers/usb/devio.c linux/drivers/usb/devio.c
--- linux-2.5.2-pre7/drivers/usb/devio.c	Fri Jan  4 19:01:17 2002
+++ linux/drivers/usb/devio.c	Fri Jan  4 19:01:21 2002
@@ -40,6 +40,7 @@
 #include <linux/smp_lock.h>
 #include <linux/signal.h>
 #include <linux/poll.h>
+#include <linux/module.h>
 #include <linux/usb.h>
 #include <linux/usbdevice_fs.h>
 #include <asm/uaccess.h>
@@ -1086,10 +1087,15 @@
 			else if (ifp->driver == 0 || ifp->driver->ioctl == 0)
 				retval = -ENOSYS;
 		}
-		if (retval == 0)
+		if (retval == 0) {
+			if (ifp->driver->owner)
+				__MOD_INC_USE_COUNT(ifp->driver->owner);
 			/* ifno might usefully be passed ... */
 			retval = ifp->driver->ioctl (ps->dev, ctrl.ioctl_code, buf);
 			/* size = min_t(int, size, retval)? */
+			if (ifp->driver->owner)
+				__MOD_DEC_USE_COUNT(ifp->driver->owner);
+		}
 	}
 
 	/* cleanup and return */
diff -urN linux-2.5.2-pre7/drivers/usb/ov511.c linux/drivers/usb/ov511.c
--- linux-2.5.2-pre7/drivers/usb/ov511.c	Fri Sep 14 14:04:07 2001
+++ linux/drivers/usb/ov511.c	Fri Jan  4 19:01:21 2002
@@ -1,13 +1,16 @@
 /*
  * OmniVision OV511 Camera-to-USB Bridge Driver
  *
- * Copyright (c) 1999-2000 Mark W. McClelland
+ * Copyright (c) 1999-2001 Mark W. McClelland
+ * Original decompression code Copyright 1998-2000 OmniVision Technologies
  * Many improvements by Bret Wallach <bwallac1@san.rr.com>
  * Color fixes by by Orion Sky Lawlor <olawlor@acm.org> (2/26/2000)
  * Snapshot code by Kevin Moore
  * OV7620 fixes by Charl P. Botha <cpbotha@ieee.org>
  * Changes by Claudio Matsuoka <claudio@conectiva.com>
- * 
+ * Original SAA7111A code by Dave Perks <dperks@ibm.net>
+ * Kernel I2C interface adapted from nt1003 driver
+ *
  * Based on the Linux CPiA driver written by Peter Pregler,
  * Scott J. Bertin and Johannes Erdfelt.
  * 
@@ -30,11 +33,9 @@
  * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-#define __NO_VERSION__
-
 #include <linux/config.h>
-#include <linux/module.h>
 #include <linux/version.h>
+#include <linux/module.h>
 #include <linux/init.h>
 #include <linux/fs.h>
 #include <linux/vmalloc.h>
@@ -42,32 +43,51 @@
 #include <linux/proc_fs.h>
 #include <linux/ctype.h>
 #include <linux/pagemap.h>
-#include <linux/usb.h>
 #include <asm/io.h>
 #include <asm/semaphore.h>
+#include <asm/processor.h>
 #include <linux/wrapper.h>
 
+#if defined (__i386__)
+	#include <asm/cpufeature.h>
+#endif
+
 #include "ov511.h"
 
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v1.28"
-#define DRIVER_AUTHOR "Mark McClelland <mwm@i.am> & Bret Wallach & Orion Sky Lawlor <olawlor@acm.org> & Kevin Moore & Charl P. Botha <cpbotha@ieee.org> & Claudio Matsuoka <claudio@conectiva.com>"
+#define DRIVER_VERSION "v1.48 for Linux 2.4"
+#define EMAIL "mmcclell@bigfoot.com"
+#define DRIVER_AUTHOR "Mark McClelland <mmcclell@bigfoot.com> & Bret Wallach \
+	& Orion Sky Lawlor <olawlor@acm.org> & Kevin Moore & Charl P. Botha \
+	<cpbotha@ieee.org> & Claudio Matsuoka <claudio@conectiva.com>"
 #define DRIVER_DESC "OV511 USB Camera Driver"
 
 #define OV511_I2C_RETRIES 3
+#define ENABLE_Y_QUANTABLE 1
+#define ENABLE_UV_QUANTABLE 1
+
+/* Pixel count * 3 bytes for RGB */
+#define MAX_FRAME_SIZE(w, h) ((w) * (h) * 3)
+#define MAX_DATA_SIZE(w, h) (MAX_FRAME_SIZE(w, h) + sizeof(struct timeval))
 
-/* Video Size 640 x 480 x 3 bytes for RGB */
-#define MAX_FRAME_SIZE (640 * 480 * 3)
-#define MAX_DATA_SIZE (MAX_FRAME_SIZE + sizeof(struct timeval))
+/* Max size * bytes per YUV420 pixel (1.5) + one extra isoc frame for safety */
+#define MAX_RAW_DATA_SIZE(w, h) ((w) * (h) * 3 / 2 + 1024)
 
-#define GET_SEGSIZE(p) ((p) == VIDEO_PALETTE_GREY ? 256 : 384)
+#define FATAL_ERROR(rc) ((rc) < 0 && (rc) != -EPERM)
 
 /* PARAMETER VARIABLES: */
-static int autoadjust = 1;    /* CCD dynamically changes exposure, etc... */
+/* (See ov511.txt for detailed descriptions of these.) */
 
-static int video_nr = -1;
+/* Sensor automatically changes brightness */
+static int autobright = 1;
+
+/* Sensor automatically changes gain */
+static int autogain = 1;
+
+/* Sensor automatically changes exposure */
+static int autoexp = 1;
 
 /* 0=no debug messages
  * 1=init/detection/unload and other significant messages,
@@ -77,27 +97,17 @@
  * 5=highly repetitive mesgs
  * NOTE: This should be changed to 0, 1, or 2 for production kernels
  */
-static int debug = 0;
+static int debug; /* = 0 */
 
 /* Fix vertical misalignment of red and blue at 640x480 */
-static int fix_rgb_offset = 0;
+static int fix_rgb_offset; /* = 0 */
 
 /* Snapshot mode enabled flag */
-static int snapshot = 0;
-
-/* Sensor detection override (global for all attached cameras) */
-static int sensor = 0;
-
-/* Increase this if you are getting "Failed to read sensor ID..." */
-static int i2c_detect_tries = 5;
-
-/* For legal values, see the OV7610/7620 specs under register Common F,
- * upper nybble  (set to 0-F) */
-static int aperture = -1;
+static int snapshot; /* = 0 */
 
 /* Force image to be read in RGB instead of BGR. This option allow
  * programs that expect RGB data (e.g. gqcam) to work with this driver. */
-static int force_rgb = 0;
+static int force_rgb; /* = 0 */
 
 /* Number of seconds before inactive buffers are deallocated */
 static int buf_timeout = 5;
@@ -105,76 +115,219 @@
 /* Number of cameras to stream from simultaneously */
 static int cams = 1;
 
-/* Prevent apps from timing out if frame is not done in time */
-static int retry_sync = 0;
-
-/* Enable compression. This is for experimentation only; compressed images
- * still cannot be decoded yet. */
-static int compress = 0;
+/* Enable compression. Needs a fast (>300 MHz) CPU. */
+static int compress; /* = 0 */
 
 /* Display test pattern - doesn't work yet either */
-static int testpat = 0;
+static int testpat; /* = 0 */
 
-/* Setting this to 1 will make the sensor output GBR422 instead on YUV420. Only
+/* Setting this to 1 will make the sensor output GBR422 instead of YUV420. Only
  * affects RGB24 mode. */
-static int sensor_gbr = 0;
+static int sensor_gbr; /* = 0 */
 
-/* Dump raw pixel data, in one of 3 formats. See ov511_dumppix() for details. */
-static int dumppix = 0;
+/* Dump raw pixel data. */
+static int dumppix; /* = 0 */
 
-MODULE_PARM(autoadjust, "i");
-MODULE_PARM_DESC(autoadjust, "CCD dynamically changes exposure");
+/* LED policy. Only works on some OV511+ cameras. 0=off, 1=on (default), 2=auto
+ * (on when open) */
+static int led = 1;
+
+/* Set this to 1 to dump the bridge register contents after initialization */
+static int dump_bridge; /* = 0 */
+
+/* Set this to 1 to dump the sensor register contents after initialization */
+static int dump_sensor; /* = 0 */
+
+/* Temporary option for debugging "works, but no image" problem. Prints the
+ * first 12 bytes of data (potentially a packet header) in each isochronous
+ * data frame. */
+static int printph; /* = 0 */
+
+/* Compression parameters - I'm not exactly sure what these do yet */
+static int phy = 0x1f;
+static int phuv = 0x05;
+static int pvy = 0x06;
+static int pvuv = 0x06;
+static int qhy = 0x14;
+static int qhuv = 0x03;
+static int qvy = 0x04;
+static int qvuv = 0x04;
+
+/* Light frequency. Set to 50 or 60 (Hz), or zero for default settings */
+static int lightfreq; /* = 0 */
+
+/* Set this to 1 to enable banding filter by default. Compensates for
+ * alternating horizontal light/dark bands caused by (usually fluorescent)
+ * lights */
+static int bandingfilter; /* = 0 */
+
+/* Pixel clock divisor */
+static int clockdiv = -1;
+
+/* Isoc packet size */
+static int packetsize = -1;
+
+/* Frame drop register (16h) */
+static int framedrop = -1;
+
+/* Allows picture settings (brightness, hue, etc...) to take effect immediately,
+ * even in the middle of a frame. This reduces the time to change settings, but
+ * can ruin frames during the change. Only affects OmniVision sensors. */
+static int fastset; /* = 0 */
+
+/* Forces the palette to a specific value. If an application requests a
+ * different palette, it will be rejected. */
+static int force_palette; /* = 0 */
+
+/* Set tuner type, if not autodetected */
+static int tuner = -1;
+
+/* Allows proper exposure of objects that are illuminated from behind. Only
+ * affects OmniVision sensors. */
+static int backlight; /* = 0 */
+
+/* If you change this, you must also change the MODULE_PARM definition */
+#define OV511_MAX_UNIT_VIDEO 16
+
+/* Allows specified minor numbers to be forced. They will be assigned in the
+ * order that devices are detected. Note that you cannot specify 0 as a minor
+ * number. If you do not specify any, the next available one will be used. This
+ * requires kernel 2.4.5 or later. */
+static int unit_video[OV511_MAX_UNIT_VIDEO];
+
+/* Remove zero-padding from uncompressed incoming data. This will compensate for
+ * the blocks of corruption that appear when the camera cannot keep up with the
+ * speed of the USB bus (eg. at low frame resolutions) */
+static int remove_zeros; /* = 0 */
+
+MODULE_PARM(autobright, "i");
+MODULE_PARM_DESC(autobright, "Sensor automatically changes brightness");
+MODULE_PARM(autogain, "i");
+MODULE_PARM_DESC(autogain, "Sensor automatically changes gain");
+MODULE_PARM(autoexp, "i");
+MODULE_PARM_DESC(autoexp, "Sensor automatically changes exposure");
 MODULE_PARM(debug, "i");
-MODULE_PARM_DESC(debug, "Debug level: 0=none, 1=init/detection, 2=warning, 3=config/control, 4=function call, 5=max");
+MODULE_PARM_DESC(debug,
+  "Debug level: 0=none, 1=inits, 2=warning, 3=config, 4=functions, 5=max");
 MODULE_PARM(fix_rgb_offset, "i");
-MODULE_PARM_DESC(fix_rgb_offset, "Fix vertical misalignment of red and blue at 640x480");
+MODULE_PARM_DESC(fix_rgb_offset,
+  "Fix vertical misalignment of red and blue at 640x480");
 MODULE_PARM(snapshot, "i");
 MODULE_PARM_DESC(snapshot, "Enable snapshot mode");
-MODULE_PARM(sensor, "i");
-MODULE_PARM_DESC(sensor, "Override sensor detection");
-MODULE_PARM(i2c_detect_tries, "i");
-MODULE_PARM_DESC(i2c_detect_tries, "Number of tries to detect sensor");
-MODULE_PARM(aperture, "i");
-MODULE_PARM_DESC(aperture, "Read the OV7610/7620 specs");
 MODULE_PARM(force_rgb, "i");
 MODULE_PARM_DESC(force_rgb, "Read RGB instead of BGR");
 MODULE_PARM(buf_timeout, "i");
 MODULE_PARM_DESC(buf_timeout, "Number of seconds before buffer deallocation");
 MODULE_PARM(cams, "i");
 MODULE_PARM_DESC(cams, "Number of simultaneous cameras");
-MODULE_PARM(retry_sync, "i");
-MODULE_PARM_DESC(retry_sync, "Prevent apps from timing out");
 MODULE_PARM(compress, "i");
-MODULE_PARM_DESC(compress, "Turn on compression (not functional yet)");
+MODULE_PARM_DESC(compress, "Turn on compression (not reliable yet)");
 MODULE_PARM(testpat, "i");
-MODULE_PARM_DESC(testpat, "Replace image with vertical bar testpattern (only partially working)");
-MODULE_PARM(sensor_gbr, "i");
-MODULE_PARM_DESC(sensor_gbr, "Make sensor output GBR422 rather than YUV420");
+MODULE_PARM_DESC(testpat,
+  "Replace image with vertical bar testpattern (only partially working)");
+
+// Temporarily removed (needs to be rewritten for new format conversion code)
+// MODULE_PARM(sensor_gbr, "i");
+// MODULE_PARM_DESC(sensor_gbr, "Make sensor output GBR422 rather than YUV420");
+
 MODULE_PARM(dumppix, "i");
-MODULE_PARM_DESC(dumppix, "Dump raw pixel data, in one of 3 formats. See ov511_dumppix() for details");
-MODULE_PARM(video_nr,"i");
+MODULE_PARM_DESC(dumppix, "Dump raw pixel data");
+MODULE_PARM(led, "i");
+MODULE_PARM_DESC(led,
+  "LED policy (OV511+ or later). 0=off, 1=on (default), 2=auto (on when open)");
+MODULE_PARM(dump_bridge, "i");
+MODULE_PARM_DESC(dump_bridge, "Dump the bridge registers");
+MODULE_PARM(dump_sensor, "i");
+MODULE_PARM_DESC(dump_sensor, "Dump the sensor registers");
+MODULE_PARM(printph, "i");
+MODULE_PARM_DESC(printph, "Print frame start/end headers");
+MODULE_PARM(phy, "i");
+MODULE_PARM_DESC(phy, "Prediction range (horiz. Y)");
+MODULE_PARM(phuv, "i");
+MODULE_PARM_DESC(phuv, "Prediction range (horiz. UV)");
+MODULE_PARM(pvy, "i");
+MODULE_PARM_DESC(pvy, "Prediction range (vert. Y)");
+MODULE_PARM(pvuv, "i");
+MODULE_PARM_DESC(pvuv, "Prediction range (vert. UV)");
+MODULE_PARM(qhy, "i");
+MODULE_PARM_DESC(qhy, "Quantization threshold (horiz. Y)");
+MODULE_PARM(qhuv, "i");
+MODULE_PARM_DESC(qhuv, "Quantization threshold (horiz. UV)");
+MODULE_PARM(qvy, "i");
+MODULE_PARM_DESC(qvy, "Quantization threshold (vert. Y)");
+MODULE_PARM(qvuv, "i");
+MODULE_PARM_DESC(qvuv, "Quantization threshold (vert. UV)");
+MODULE_PARM(lightfreq, "i");
+MODULE_PARM_DESC(lightfreq,
+  "Light frequency. Set to 50 or 60 Hz, or zero for default settings");
+MODULE_PARM(bandingfilter, "i");
+MODULE_PARM_DESC(bandingfilter,
+  "Enable banding filter (to reduce effects of fluorescent lighting)");
+MODULE_PARM(clockdiv, "i");
+MODULE_PARM_DESC(clockdiv, "Force pixel clock divisor to a specific value");
+MODULE_PARM(packetsize, "i");
+MODULE_PARM_DESC(packetsize, "Force a specific isoc packet size");
+MODULE_PARM(framedrop, "i");
+MODULE_PARM_DESC(framedrop, "Force a specific frame drop register setting");
+MODULE_PARM(fastset, "i");
+MODULE_PARM_DESC(fastset, "Allows picture settings to take effect immediately");
+MODULE_PARM(force_palette, "i");
+MODULE_PARM_DESC(force_palette, "Force the palette to a specific value");
+MODULE_PARM(tuner, "i");
+MODULE_PARM_DESC(tuner, "Set tuner type, if not autodetected");
+MODULE_PARM(backlight, "i");
+MODULE_PARM_DESC(backlight, "For objects that are lit from behind");
+MODULE_PARM(unit_video, "0-16i");
+MODULE_PARM_DESC(unit_video,
+  "Force use of specific minor number(s). 0 is not allowed.");
+MODULE_PARM(remove_zeros, "i");
+MODULE_PARM_DESC(remove_zeros,
+  "Remove zero-padding from uncompressed incoming data");
 
-MODULE_AUTHOR( DRIVER_AUTHOR );
-MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
 
 static struct usb_driver ov511_driver;
 
-/* I know, I know, global variables suck. This is only a temporary hack */
-int output_offset;
+static struct ov51x_decomp_ops *ov511_decomp_ops;
+static struct ov51x_decomp_ops *ov511_mmx_decomp_ops;
+static struct ov51x_decomp_ops *ov518_decomp_ops;
+static struct ov51x_decomp_ops *ov518_mmx_decomp_ops;
+
+/* Number of times to retry a failed I2C transaction. Increase this if you
+ * are getting "Failed to read sensor ID..." */
+static int i2c_detect_tries = 5;
+
+/* MMX support is present in kernel and CPU. Checked upon decomp module load. */
+static int ov51x_mmx_available;
+
+/* Function prototypes */
+static void ov51x_clear_snapshot(struct usb_ov511 *);
+static int ov51x_check_snapshot(struct usb_ov511 *);
+static inline int sensor_get_picture(struct usb_ov511 *, 
+				     struct video_picture *);
+static int sensor_get_exposure(struct usb_ov511 *, unsigned char *);
+static int ov511_control_ioctl(struct inode *, struct file *, unsigned int,
+			       unsigned long);
 
 /**********************************************************************
  * List of known OV511-based cameras
  **********************************************************************/
 
 static struct cam_list clist[] = {
-	{   0, "generic model (no ID)" },
+	{   0, "Generic Camera (no ID)" },
+	{   1, "Mustek WCam 3X" },
 	{   3, "D-Link DSB-C300" },
-	{   4, "generic OV511/OV7610" },
+	{   4, "Generic OV511/OV7610" },
 	{   5, "Puretek PT-6007" },
+	{   6, "Lifeview USB Life TV (NTSC)" },
 	{  21, "Creative Labs WebCam 3" },
 	{  36, "Koala-Cam" },
-	{  38, "Lifeview USB Life TV" },	/* No support yet! */
+	{  38, "Lifeview USB Life TV" },
+	{  41, "Samsung Anycam MPC-M10" },
+	{  43, "Mtekvision Zeca MV402" },
+	{  46, "Suma eON" },
 	{ 100, "Lifeview RoboCam" },
 	{ 102, "AverMedia InterCam Elite" },
 	{ 112, "MediaForte MV300" },	/* or OV7110 evaluation kit */
@@ -182,9 +335,11 @@
 };
 
 static __devinitdata struct usb_device_id device_table [] = {
-	{ USB_DEVICE(0x05a9, 0x0511) },  /* OV511 */
-	{ USB_DEVICE(0x05a9, 0xA511) },  /* OV511+ */
-	{ USB_DEVICE(0x0813, 0x0002) },  /* Intel Play Me2Cam OV511+ */
+	{ USB_DEVICE(VEND_OMNIVISION, PROD_OV511) },
+	{ USB_DEVICE(VEND_OMNIVISION, PROD_OV511PLUS) },
+	{ USB_DEVICE(VEND_OMNIVISION, PROD_OV518) },
+	{ USB_DEVICE(VEND_OMNIVISION, PROD_OV518PLUS) },
+	{ USB_DEVICE(VEND_MATTEL, PROD_ME2CAM) },
 	{ }  /* Terminating entry */
 };
 
@@ -212,6 +367,11 @@
 };
 #endif
 
+static unsigned char yQuanTable511[] = OV511_YQUANTABLE;
+static unsigned char uvQuanTable511[] = OV511_UVQUANTABLE;
+static unsigned char yQuanTable518[] = OV518_YQUANTABLE;
+static unsigned char uvQuanTable518[] = OV518_UVQUANTABLE;
+
 /**********************************************************************
  *
  * Memory management
@@ -231,7 +391,8 @@
 /* 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)
+static inline unsigned long 
+uvirt_to_kva(pgd_t *pgd, unsigned long adr)
 {
 	unsigned long ret = 0UL;
 	pmd_t *pmd;
@@ -243,7 +404,8 @@
 			ptep = pte_offset(pmd, adr);
 			pte = *ptep;
 			if (pte_present(pte)) {
-				ret = (unsigned long) page_address(pte_page(pte));
+				ret = (unsigned long) 
+				      page_address(pte_page(pte));
 				ret |= (adr & (PAGE_SIZE - 1));
 			}
 		}
@@ -256,7 +418,8 @@
  * 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)
+static inline unsigned long 
+kvirt_to_pa(unsigned long adr)
 {
 	unsigned long va, kva, ret;
 
@@ -266,7 +429,8 @@
 	return ret;
 }
 
-static void *rvmalloc(unsigned long size)
+static void *
+rvmalloc(unsigned long size)
 {
 	void *mem;
 	unsigned long adr, page;
@@ -294,7 +458,8 @@
 	return mem;
 }
 
-static void rvfree(void *mem, unsigned long size)
+static void 
+rvfree(void *mem, unsigned long size)
 {
 	unsigned long adr, page;
 
@@ -329,70 +494,90 @@
 
 #define YES_NO(x) ((x) ? "yes" : "no")
 
-static int ov511_read_proc(char *page, char **start, off_t off,
-                          int count, int *eof, void *data)
+/* /proc/video/ov511/<minor#>/info */
+static int 
+ov511_read_proc_info(char *page, char **start, off_t off, int count, int *eof,
+		     void *data)
 {
 	char *out = page;
 	int i, j, len;
 	struct usb_ov511 *ov511 = data;
+	struct video_picture p;
+	unsigned char exp;
+
+	if (!ov511 || !ov511->dev)
+		return -ENODEV;
+
+	sensor_get_picture(ov511, &p);
+	sensor_get_exposure(ov511, &exp);
 
 	/* IMPORTANT: This output MUST be kept under PAGE_SIZE
 	 *            or we need to get more sophisticated. */
 
-	out += sprintf (out, "driver_version  : %s\n", DRIVER_VERSION);
-	out += sprintf (out, "custom_id       : %d\n", ov511->customid);
-	out += sprintf (out, "model           : %s\n", ov511->desc ?
-		clist[ov511->desc].description : "unknown");
-	out += sprintf (out, "streaming       : %s\n", YES_NO (ov511->streaming));
-	out += sprintf (out, "grabbing        : %s\n", YES_NO (ov511->grabbing));
-	out += sprintf (out, "compress        : %s\n", YES_NO (ov511->compress));
-	out += sprintf (out, "subcapture      : %s\n", YES_NO (ov511->sub_flag));
-	out += sprintf (out, "sub_size        : %d %d %d %d\n",
-		ov511->subx, ov511->suby, ov511->subw, ov511->subh);
-	out += sprintf (out, "data_format     : %s\n", force_rgb ? "RGB" : "BGR");
-	out += sprintf (out, "brightness      : %d\n", ov511->brightness >> 8);
-	out += sprintf (out, "colour          : %d\n", ov511->colour >> 8);
-	out += sprintf (out, "contrast        : %d\n", ov511->contrast >> 8);
-	out += sprintf (out, "num_frames      : %d\n", OV511_NUMFRAMES);
+	out += sprintf(out, "driver_version  : %s\n", DRIVER_VERSION);
+	out += sprintf(out, "custom_id       : %d\n", ov511->customid);
+	out += sprintf(out, "model           : %s\n", ov511->desc ?
+		       clist[ov511->desc].description : "unknown");
+	out += sprintf(out, "streaming       : %s\n", YES_NO(ov511->streaming));
+	out += sprintf(out, "grabbing        : %s\n", YES_NO(ov511->grabbing));
+	out += sprintf(out, "compress        : %s\n", YES_NO(ov511->compress));
+	out += sprintf(out, "subcapture      : %s\n", YES_NO(ov511->sub_flag));
+	out += sprintf(out, "sub_size        : %d %d %d %d\n",
+		       ov511->subx, ov511->suby, ov511->subw, ov511->subh);
+	out += sprintf(out, "data_format     : %s\n",
+		       force_rgb ? "RGB" : "BGR");
+	out += sprintf(out, "brightness      : %d\n", p.brightness >> 8);
+	out += sprintf(out, "colour          : %d\n", p.colour >> 8);
+	out += sprintf(out, "contrast        : %d\n", p.contrast >> 8);
+	out += sprintf(out, "hue             : %d\n", p.hue >> 8);
+	out += sprintf(out, "exposure        : %d\n", exp);
+	out += sprintf(out, "num_frames      : %d\n", OV511_NUMFRAMES);
 	for (i = 0; i < OV511_NUMFRAMES; i++) {
-		out += sprintf (out, "frame           : %d\n", i);
-		out += sprintf (out, "  depth         : %d\n",
-			ov511->frame[i].depth);
-		out += sprintf (out, "  size          : %d %d\n",
-			ov511->frame[i].width, ov511->frame[i].height);
-		out += sprintf (out, "  format        : ");
+		out += sprintf(out, "frame           : %d\n", i);
+		out += sprintf(out, "  depth         : %d\n",
+			       ov511->frame[i].depth);
+		out += sprintf(out, "  size          : %d %d\n",
+			       ov511->frame[i].width, ov511->frame[i].height);
+		out += sprintf(out, "  format        : ");
 		for (j = 0; plist[j].num >= 0; j++) {
 			if (plist[j].num == ov511->frame[i].format) {
-				out += sprintf (out, "%s\n", plist[j].name);
+				out += sprintf(out, "%s\n", plist[j].name);
 				break;
 			}
 		}
 		if (plist[j].num < 0)
-			out += sprintf (out, "unknown\n");
-		out += sprintf (out, "  segsize       : %d\n",
-			ov511->frame[i].segsize);
-		out += sprintf (out, "  data_buffer   : 0x%p\n",
-			ov511->frame[i].data);
-	}
-	out += sprintf (out, "snap_enabled    : %s\n", YES_NO (ov511->snap_enabled));
-	out += sprintf (out, "bridge          : %s\n",
-		ov511->bridge == BRG_OV511 ? "OV511" :
-		ov511->bridge == BRG_OV511PLUS ? "OV511+" :
-		"unknown");
-	out += sprintf (out, "sensor          : %s\n",
-		ov511->sensor == SEN_OV6620 ? "OV6620" :
-		ov511->sensor == SEN_OV7610 ? "OV7610" :
-		ov511->sensor == SEN_OV7620 ? "OV7620" :
-		ov511->sensor == SEN_OV7620AE ? "OV7620AE" :
-		"unknown");
-	out += sprintf (out, "packet_size     : %d\n", ov511->packet_size);
-	out += sprintf (out, "framebuffer     : 0x%p\n", ov511->fbuf);
-	
+			out += sprintf(out, "unknown\n");
+		out += sprintf(out, "  data_buffer   : 0x%p\n",
+			       ov511->frame[i].data);
+	}
+	out += sprintf(out, "snap_enabled    : %s\n",
+		       YES_NO(ov511->snap_enabled));
+	out += sprintf(out, "bridge          : %s\n",
+		       ov511->bridge == BRG_OV511 ? "OV511" :
+			ov511->bridge == BRG_OV511PLUS ? "OV511+" :
+			ov511->bridge == BRG_OV518 ? "OV518" :
+			ov511->bridge == BRG_OV518PLUS ? "OV518+" :
+			"unknown");
+	out += sprintf(out, "sensor          : %s\n",
+		       ov511->sensor == SEN_OV6620 ? "OV6620" :
+			ov511->sensor == SEN_OV6630 ? "OV6630" :
+			ov511->sensor == SEN_OV7610 ? "OV7610" :
+			ov511->sensor == SEN_OV7620 ? "OV7620" :
+			ov511->sensor == SEN_OV7620AE ? "OV7620AE" :
+			ov511->sensor == SEN_OV8600 ? "OV8600" :
+			ov511->sensor == SEN_KS0127 ? "KS0127" :
+			ov511->sensor == SEN_KS0127B ? "KS0127B" :
+			ov511->sensor == SEN_SAA7111A ? "SAA7111A" :
+			"unknown");
+	out += sprintf(out, "packet_size     : %d\n", ov511->packet_size);
+	out += sprintf(out, "framebuffer     : 0x%p\n", ov511->fbuf);
+
 	len = out - page;
 	len -= off;
 	if (len < count) {
 		*eof = 1;
-		if (len <= 0) return 0;
+		if (len <= 0)
+			return 0;
 	} else
 		len = count;
 
@@ -401,69 +586,160 @@
 	return len;
 }
 
-static int ov511_write_proc(struct file *file, const char *buffer,
-                           unsigned long count, void *data)
+/* /proc/video/ov511/<minor#>/button
+ *
+ * When the camera's button is pressed, the output of this will change from a
+ * 0 to a 1 (ASCII). It will retain this value until it is read, after which
+ * it will reset to zero.
+ * 
+ * SECURITY NOTE: Since reading this file can change the state of the snapshot
+ * status, it is important for applications that open it to keep it locked
+ * against access by other processes, using flock() or a similar mechanism. No
+ * locking is provided by this driver.
+ */
+static int 
+ov511_read_proc_button(char *page, char **start, off_t off, int count, int *eof,
+		       void *data)
 {
-	return -EINVAL;
+	char *out = page;
+	int len, status;
+	struct usb_ov511 *ov511 = data;
+
+	if (!ov511 || !ov511->dev)
+		return -ENODEV;
+
+	status = ov51x_check_snapshot(ov511);
+	out += sprintf(out, "%d", status);
+
+	if (status)
+		ov51x_clear_snapshot(ov511);
+
+	len = out - page;
+	len -= off;
+	if (len < count) {
+		*eof = 1;
+		if (len <= 0)
+			return 0;
+	} else {
+		len = count;
+	}
+
+	*start = page + off;
+
+	return len;
 }
 
-static void create_proc_ov511_cam (struct usb_ov511 *ov511)
+static void 
+create_proc_ov511_cam(struct usb_ov511 *ov511)
 {
-	char name[7];
-	struct proc_dir_entry *ent;
-	
+	char dirname[4];
+
 	if (!ov511_proc_entry || !ov511)
 		return;
 
-	sprintf(name, "video%d", ov511->vdev.minor);
-	PDEBUG (4, "creating /proc/video/ov511/%s", name);
-	
-	ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, ov511_proc_entry);
+	/* Create per-device directory */
+	sprintf(dirname, "%d", ov511->vdev.minor);
+	PDEBUG(4, "creating /proc/video/ov511/%s/", dirname);
+	ov511->proc_devdir = create_proc_entry(dirname, S_IFDIR,
+		ov511_proc_entry);
+	if (!ov511->proc_devdir)
+		return;
 
-	if (!ent)
+	/* Create "info" entry (human readable device information) */
+	PDEBUG(4, "creating /proc/video/ov511/%s/info", dirname);
+	ov511->proc_info = create_proc_read_entry("info",
+		S_IFREG|S_IRUGO|S_IWUSR, ov511->proc_devdir,
+		ov511_read_proc_info, ov511);
+	if (!ov511->proc_info)
 		return;
 
-	ent->data = ov511;
-	ent->read_proc = ov511_read_proc;
-	ent->write_proc = ov511_write_proc;
-	ov511->proc_entry = ent;
+	/* Don't create it if old snapshot mode on (would cause race cond.) */
+	if (!snapshot) {
+		/* Create "button" entry (snapshot button status) */
+		PDEBUG(4, "creating /proc/video/ov511/%s/button", dirname);
+		ov511->proc_button = create_proc_read_entry("button",
+			S_IFREG|S_IRUGO|S_IWUSR, ov511->proc_devdir,
+			ov511_read_proc_button, ov511);
+		if (!ov511->proc_button)
+			return;
+	}
+
+	/* Create "control" entry (ioctl() interface) */
+	PDEBUG(4, "creating /proc/video/ov511/%s/control", dirname);
+	lock_kernel();
+	ov511->proc_control = create_proc_entry("control",
+		S_IFREG|S_IRUGO|S_IWUSR, ov511->proc_devdir);
+	if (!ov511->proc_control) {
+		unlock_kernel();
+		return;
+	}
+	ov511->proc_control->proc_fops->ioctl = ov511_control_ioctl;
+	ov511->proc_control->data = ov511;
+	unlock_kernel();
 }
 
-static void destroy_proc_ov511_cam (struct usb_ov511 *ov511)
+static void 
+destroy_proc_ov511_cam(struct usb_ov511 *ov511)
 {
-	char name[7];
+	char dirname[4];
 	
-	if (!ov511 || !ov511->proc_entry)
+	if (!ov511 || !ov511->proc_devdir)
 		return;
-	
-	sprintf(name, "video%d", ov511->vdev.minor);
-	PDEBUG (4, "destroying %s", name);
-	remove_proc_entry(name, ov511_proc_entry);
-	ov511->proc_entry = NULL;
+
+	sprintf(dirname, "%d", ov511->vdev.minor);
+
+	/* Destroy "control" entry */
+	if (ov511->proc_control) {
+		PDEBUG(4, "destroying /proc/video/ov511/%s/control", dirname);
+		remove_proc_entry("control", ov511->proc_devdir);
+		ov511->proc_control = NULL;
+	}
+
+	/* Destroy "button" entry */
+	if (ov511->proc_button) {
+		PDEBUG(4, "destroying /proc/video/ov511/%s/button", dirname);
+		remove_proc_entry("button", ov511->proc_devdir);
+		ov511->proc_button = NULL;
+	}
+
+	/* Destroy "info" entry */
+	if (ov511->proc_info) {
+		PDEBUG(4, "destroying /proc/video/ov511/%s/info", dirname);
+		remove_proc_entry("info", ov511->proc_devdir);
+		ov511->proc_info = NULL;
+	}
+
+	/* Destroy per-device directory */
+	PDEBUG(4, "destroying /proc/video/ov511/%s/", dirname);
+	remove_proc_entry(dirname, ov511_proc_entry);
+	ov511->proc_devdir = NULL;
 }
 
-static void proc_ov511_create(void)
+static void 
+proc_ov511_create(void)
 {
 	/* No current standard here. Alan prefers /proc/video/ as it keeps
 	 * /proc "less cluttered than /proc/randomcardifoundintheshed/"
 	 * -claudio
 	 */
 	if (video_proc_entry == NULL) {
-		err("Unable to initialise /proc/video/ov511");
+		err("Error: /proc/video/ does not exist");
 		return;
 	}
 
-	ov511_proc_entry = create_proc_entry("ov511", S_IFDIR, video_proc_entry);
+	ov511_proc_entry = create_proc_entry("ov511", S_IFDIR,
+					     video_proc_entry);
 
 	if (ov511_proc_entry)
 		ov511_proc_entry->owner = THIS_MODULE;
 	else
-		err("Unable to initialise /proc/ov511");
+		err("Unable to create /proc/video/ov511");
 }
 
-static void proc_ov511_destroy(void)
+static void 
+proc_ov511_destroy(void)
 {
-	PDEBUG (3, "removing /proc/video/ov511");
+	PDEBUG(3, "removing /proc/video/ov511");
 
 	if (ov511_proc_entry == NULL)
 		return;
@@ -474,23 +750,22 @@
 
 /**********************************************************************
  *
- * Camera interface
+ * Register I/O
  *
  **********************************************************************/
 
-static int ov511_reg_write(struct usb_device *dev,
-			   unsigned char reg,
-			   unsigned char value)
+static int 
+ov511_reg_write(struct usb_device *dev, unsigned char reg, unsigned char value)
 {
 	int rc;
 
-	rc = usb_control_msg(dev,
-		usb_sndctrlpipe(dev, 0),
-		2 /* REG_IO */,
-		USB_TYPE_CLASS | USB_RECIP_DEVICE,
-		0, (__u16)reg, &value, 1, HZ);	
+	PDEBUG(5, "0x%02X:0x%02X", reg, value);
 
-	PDEBUG(5, "reg write: 0x%02X:0x%02X, 0x%x", reg, value, rc);
+	rc = usb_control_msg(dev,
+			     usb_sndctrlpipe(dev, 0),
+			     2 /* REG_IO */,
+			     USB_TYPE_CLASS | USB_RECIP_DEVICE,
+			     0, (__u16)reg, &value, 1, HZ);	
 
 	if (rc < 0)
 		err("reg write: error %d", rc);
@@ -499,18 +774,19 @@
 }
 
 /* returns: negative is error, pos or zero is data */
-static int ov511_reg_read(struct usb_device *dev, unsigned char reg)
+static int 
+ov511_reg_read(struct usb_device *dev, unsigned char reg)
 {
 	int rc;
 	unsigned char buffer[1];
 
 	rc = usb_control_msg(dev,
-		usb_rcvctrlpipe(dev, 0),
-		2 /* REG_IO */,
-		USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE,
-		0, (__u16)reg, buffer, 1, HZ);
+			     usb_rcvctrlpipe(dev, 0),
+			     2 /* REG_IO */,
+			     USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE,
+			     0, (__u16)reg, buffer, 1, HZ);
                                
-	PDEBUG(5, "reg read: 0x%02X:0x%02X", reg, buffer[0]);
+	PDEBUG(5, "0x%02X:0x%02X", reg, buffer[0]);
 	
 	if (rc < 0) {
 		err("reg read: error %d", rc);
@@ -520,18 +796,197 @@
 	}
 }
 
-static int ov511_i2c_write(struct usb_device *dev,
-			   unsigned char reg,
-			   unsigned char value)
+/*
+ * Writes bits at positions specified by mask to a reg. Bits that are in
+ * the same position as 1's in "mask" are cleared and set to "value". Bits
+ * that are in the same position as 0's in "mask" are preserved, regardless 
+ * of their respective state in "value".
+ */
+static int 
+ov511_reg_write_mask(struct usb_device *dev,
+		     unsigned char reg,
+		     unsigned char value,
+		     unsigned char mask)
+{
+	int ret;
+	unsigned char oldval, newval;
+
+	ret = ov511_reg_read(dev, reg);
+	if (ret < 0)
+		return ret;
+
+	oldval = (unsigned char) ret;
+	oldval &= (~mask);		/* Clear the masked bits */
+	value &= mask;			/* Enforce mask on value */
+	newval = oldval | value;	/* Set the desired bits */
+
+	return (ov511_reg_write(dev, reg, newval));
+}
+
+/* Writes multiple (n) values to a single register. Only valid with certain
+ * registers (0x30 and 0xc4 - 0xce). Used for writing 16 and 24-bit values. */
+static int 
+ov518_reg_write_multi(struct usb_device *dev,
+		      unsigned char reg,
+		      unsigned char *values,
+		      int n)
+{
+	int rc;
+
+	PDEBUG(5, "0x%02X:[multiple], n=%d", reg, n);  // FIXME
+
+	if (values == NULL) {
+		err("reg write multiple: NULL buffer");
+		return -EINVAL;
+	}
+
+	rc = usb_control_msg(dev,
+			     usb_sndctrlpipe(dev, 0),
+			     2 /* REG_IO */,
+			     USB_TYPE_CLASS | USB_RECIP_DEVICE,
+			     0, (__u16)reg, values, n, HZ);	
+
+	if (rc < 0)
+		err("reg write multiple: error %d", rc);
+
+	return rc;
+}
+
+static int 
+ov511_upload_quan_tables(struct usb_device *dev)
+{
+	unsigned char *pYTable = yQuanTable511;
+	unsigned char *pUVTable = uvQuanTable511;
+	unsigned char val0, val1;
+	int i, rc, reg = OV511_OMNICE_Y_LUT_BEGIN;
+
+	PDEBUG(4, "Uploading quantization tables");
+
+	for (i = 0; i < OV511_QUANTABLESIZE / 2; i++)
+	{
+		if (ENABLE_Y_QUANTABLE)
+		{
+			val0 = *pYTable++;
+			val1 = *pYTable++;
+			val0 &= 0x0f;
+			val1 &= 0x0f;
+			val0 |= val1 << 4;
+			rc = ov511_reg_write(dev, reg, val0);
+			if (rc < 0)
+				return rc;
+		}
+
+		if (ENABLE_UV_QUANTABLE)
+		{
+			val0 = *pUVTable++;
+			val1 = *pUVTable++;
+			val0 &= 0x0f;
+			val1 &= 0x0f;
+			val0 |= val1 << 4;
+			rc = ov511_reg_write(dev, reg + OV511_QUANTABLESIZE / 2,
+				val0);
+			if (rc < 0)
+				return rc;
+		}
+
+		reg++;
+	}
+
+	return 0;
+}
+
+/* OV518 quantization tables are 8x4 (instead of 8x8) */
+static int 
+ov518_upload_quan_tables(struct usb_device *dev)
+{
+	unsigned char *pYTable = yQuanTable518;
+	unsigned char *pUVTable = uvQuanTable518;
+	unsigned char val0, val1;
+	int i, rc, reg = OV511_OMNICE_Y_LUT_BEGIN;
+
+	PDEBUG(4, "Uploading quantization tables");
+
+	for (i = 0; i < OV518_QUANTABLESIZE / 2; i++)
+	{
+		if (ENABLE_Y_QUANTABLE)
+		{
+			val0 = *pYTable++;
+			val1 = *pYTable++;
+			val0 &= 0x0f;
+			val1 &= 0x0f;
+			val0 |= val1 << 4;
+			rc = ov511_reg_write(dev, reg, val0);
+			if (rc < 0)
+				return rc;
+		}
+
+		if (ENABLE_UV_QUANTABLE)
+		{
+			val0 = *pUVTable++;
+			val1 = *pUVTable++;
+			val0 &= 0x0f;
+			val1 &= 0x0f;
+			val0 |= val1 << 4;
+			rc = ov511_reg_write(dev, reg + OV518_QUANTABLESIZE / 2,
+				val0);
+			if (rc < 0)
+				return rc;
+		}
+
+		reg++;
+	}
+
+	return 0;
+}
+
+/* NOTE: Do not call this function directly!
+ * The OV518 I2C I/O procedure is different, hence, this function.
+ * This is normally only called from ov51x_i2c_write(). Note that this function
+ * always succeeds regardless of whether the sensor is present and working.
+ */
+static int 
+ov518_i2c_write_internal(struct usb_device *dev,
+			 unsigned char reg,
+			 unsigned char value)
+{
+	int rc;
+
+	PDEBUG(5, "0x%02X:0x%02X", reg, value);
+
+	/* Select camera register */
+	rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_3_BYTE, reg);
+	if (rc < 0) goto error;
+
+	/* Write "value" to I2C data port of OV511 */
+	rc = ov511_reg_write(dev, OV511_REG_I2C_DATA_PORT, value);
+	if (rc < 0) goto error;
+
+	/* Initiate 3-byte write cycle */
+	rc = ov511_reg_write(dev, OV518_REG_I2C_CONTROL, 0x01);
+	if (rc < 0) goto error;
+
+	return 0;
+
+error:
+	err("ov518 i2c write: error %d", rc);
+	return rc;
+}
+
+/* NOTE: Do not call this function directly! */
+static int 
+ov511_i2c_write_internal(struct usb_device *dev,
+			 unsigned char reg,
+			 unsigned char value)
 {
 	int rc, retries;
 
-	PDEBUG(5, "i2c write: 0x%02X:0x%02X", reg, value);
+	PDEBUG(5, "0x%02X:0x%02X", reg, value);
 
 	/* Three byte write cycle */
 	for (retries = OV511_I2C_RETRIES; ; ) {
 		/* Select camera register */
-		rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_3_BYTE, reg);
+		rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_3_BYTE,
+				     reg);
 		if (rc < 0) goto error;
 
 		/* Write "value" to I2C data port of OV511 */
@@ -566,15 +1021,51 @@
 	return rc;
 }
 
-/* returns: negative is error, pos or zero is data */
-static int ov511_i2c_read(struct usb_device *dev, unsigned char reg)
+/* NOTE: Do not call this function directly!
+ * The OV518 I2C I/O procedure is different, hence, this function.
+ * This is normally only called from ov51x_i2c_read(). Note that this function
+ * always succeeds regardless of whether the sensor is present and working.
+ */
+static int 
+ov518_i2c_read_internal(struct usb_device *dev, unsigned char reg)
+{
+	int rc, value;
+
+	/* Select camera register */
+	rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_2_BYTE, reg);
+	if (rc < 0) goto error;
+
+	/* Initiate 2-byte write cycle */
+	rc = ov511_reg_write(dev, OV518_REG_I2C_CONTROL, 0x03);
+	if (rc < 0) goto error;
+
+	/* Initiate 2-byte read cycle */
+	rc = ov511_reg_write(dev, OV518_REG_I2C_CONTROL, 0x05);
+	if (rc < 0) goto error;
+
+	value = ov511_reg_read(dev, OV511_REG_I2C_DATA_PORT);
+
+	PDEBUG(5, "0x%02X:0x%02X", reg, value);
+
+	return value;
+
+error:
+	err("ov518 i2c read: error %d", rc);
+	return rc;
+}
+
+/* NOTE: Do not call this function directly!
+ * returns: negative is error, pos or zero is data */
+static int 
+ov511_i2c_read_internal(struct usb_device *dev, unsigned char reg)
 {
 	int rc, value, retries;
 
 	/* Two byte write cycle */
 	for (retries = OV511_I2C_RETRIES; ; ) {
 		/* Select camera register */
-		rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_2_BYTE, reg);
+		rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_2_BYTE,
+				     reg);
 		if (rc < 0) goto error;
 
 		/* Initiate 2-byte write cycle */
@@ -624,9 +1115,9 @@
 
 	value = ov511_reg_read(dev, OV511_REG_I2C_DATA_PORT);
 
-	PDEBUG(5, "i2c read: 0x%02X:0x%02X", reg, value);
+	PDEBUG(5, "0x%02X:0x%02X", reg, value);
 		
-	/* This is needed to make ov511_i2c_write() work */
+	/* This is needed to make ov51x_i2c_write() work */
 	rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x05);
 	if (rc < 0)
 		goto error;
@@ -638,19 +1129,207 @@
 	return rc;
 }
 
-static int ov511_write_regvals(struct usb_device *dev,
-			       struct ov511_regvals * pRegvals)
+/* returns: negative is error, pos or zero is data */
+static int 
+ov51x_i2c_read(struct usb_ov511 *ov511, unsigned char reg)
+{
+	int rc;
+	struct usb_device *dev = ov511->dev;
+
+	down(&ov511->i2c_lock);
+
+	if (dev->descriptor.idProduct == PROD_OV518 ||
+	    dev->descriptor.idProduct == PROD_OV518PLUS)
+		rc = ov518_i2c_read_internal(dev, reg);
+	else
+		rc = ov511_i2c_read_internal(dev, reg);
+
+	up(&ov511->i2c_lock);
+
+	return rc;
+}
+
+static int 
+ov51x_i2c_write(struct usb_ov511 *ov511,
+		unsigned char reg,
+		unsigned char value)
+{
+	int rc;
+	struct usb_device *dev = ov511->dev;
+
+	down(&ov511->i2c_lock);
+
+	if (dev->descriptor.idProduct == PROD_OV518 ||
+	    dev->descriptor.idProduct == PROD_OV518PLUS)
+		rc = ov518_i2c_write_internal(dev, reg, value);
+	else
+		rc = ov511_i2c_write_internal(dev, reg, value);
+
+	up(&ov511->i2c_lock);
+
+	return rc;
+}
+
+/* Do not call this function directly! */
+static int 
+ov51x_i2c_write_mask_internal(struct usb_device *dev,
+			      unsigned char reg,
+			      unsigned char value,
+			      unsigned char mask)
+{
+	int rc;
+	unsigned char oldval, newval;
+
+	if (mask == 0xff) {
+		newval = value;
+	} else {
+		if (dev->descriptor.idProduct == PROD_OV518 ||
+		    dev->descriptor.idProduct == PROD_OV518PLUS)
+			rc = ov518_i2c_read_internal(dev, reg);
+		else
+			rc = ov511_i2c_read_internal(dev, reg);
+		if (rc < 0)
+			return rc;
+
+		oldval = (unsigned char) rc;
+		oldval &= (~mask);		/* Clear the masked bits */
+		value &= mask;			/* Enforce mask on value */
+		newval = oldval | value;	/* Set the desired bits */
+	}
+
+	if (dev->descriptor.idProduct == PROD_OV518 ||
+	    dev->descriptor.idProduct == PROD_OV518PLUS)
+		return (ov518_i2c_write_internal(dev, reg, newval));
+	else
+		return (ov511_i2c_write_internal(dev, reg, newval));
+}
+
+/* Writes bits at positions specified by mask to an I2C reg. Bits that are in
+ * the same position as 1's in "mask" are cleared and set to "value". Bits
+ * that are in the same position as 0's in "mask" are preserved, regardless 
+ * of their respective state in "value".
+ */
+static int 
+ov51x_i2c_write_mask(struct usb_ov511 *ov511,
+		     unsigned char reg,
+		     unsigned char value,
+		     unsigned char mask)
+{
+	int rc;
+	struct usb_device *dev = ov511->dev;
+
+	down(&ov511->i2c_lock);
+	rc = ov51x_i2c_write_mask_internal(dev, reg, value, mask);
+	up(&ov511->i2c_lock);
+
+	return rc;
+}
+
+/* Write to a specific I2C slave ID and register, using the specified mask */
+static int 
+ov51x_i2c_write_slave(struct usb_ov511 *ov511,
+		      unsigned char slave,
+		      unsigned char reg,
+		      unsigned char value,
+		      unsigned char mask)
+{
+	int rc = 0;
+	struct usb_device *dev = ov511->dev;
+
+	down(&ov511->i2c_lock);
+
+	/* Set new slave IDs */
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, slave) < 0) {
+		rc = -EIO;
+		goto out;
+	}
+
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, slave + 1) < 0) {
+		rc = -EIO;
+		goto out;
+	}
+
+	rc = ov51x_i2c_write_mask_internal(dev, reg, value, mask);
+	/* Don't bail out yet if error; IDs must be restored */
+
+	/* Restore primary IDs */
+	slave = ov511->primary_i2c_slave;
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, slave) < 0) {
+		rc = -EIO;
+		goto out;
+	}
+
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, slave + 1) < 0) {
+		rc = -EIO;
+		goto out;
+	}
+
+out:
+	up(&ov511->i2c_lock);
+	return rc;
+}
+
+/* Read from a specific I2C slave ID and register */
+static int 
+ov51x_i2c_read_slave(struct usb_ov511 *ov511,
+		     unsigned char slave,
+		     unsigned char reg)
+{
+	int rc;
+	struct usb_device *dev = ov511->dev;
+
+	down(&ov511->i2c_lock);
+
+	/* Set new slave IDs */
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, slave) < 0) {
+		rc = -EIO;
+		goto out;
+	}
+
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, slave + 1) < 0) {
+		rc = -EIO;
+		goto out;
+	}
+
+	if (dev->descriptor.idProduct == PROD_OV518 ||
+	    dev->descriptor.idProduct == PROD_OV518PLUS)
+		rc = ov518_i2c_read_internal(dev, reg);
+	else
+		rc = ov511_i2c_read_internal(dev, reg);
+	/* Don't bail out yet if error; IDs must be restored */
+
+	/* Restore primary IDs */
+	slave = ov511->primary_i2c_slave;
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, slave) < 0) {
+		rc = -EIO;
+		goto out;
+	}
+
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, slave + 1) < 0) {
+		rc = -EIO;
+		goto out;
+	}
+
+out:
+	up(&ov511->i2c_lock);
+	return rc;
+}
+
+static int 
+ov511_write_regvals(struct usb_ov511 *ov511,
+		    struct ov511_regvals * pRegvals)
 {
 	int rc;
+	struct usb_device *dev = ov511->dev;
 
 	while (pRegvals->bus != OV511_DONE_BUS) {
 		if (pRegvals->bus == OV511_REG_BUS) {
 			if ((rc = ov511_reg_write(dev, pRegvals->reg,
-			                           pRegvals->val)) < 0)
+			                          pRegvals->val)) < 0)
 				goto error;
 		} else if (pRegvals->bus == OV511_I2C_BUS) {
-			if ((rc = ov511_i2c_write(dev, pRegvals->reg, 
-			                           pRegvals->val)) < 0)
+			if ((rc = ov51x_i2c_write(ov511, pRegvals->reg, 
+			                          pRegvals->val)) < 0)
 				goto error;
 		} else {
 			err("Bad regval array");
@@ -667,65 +1346,92 @@
 }
 
 #ifdef OV511_DEBUG 
-static void ov511_dump_i2c_range(struct usb_device *dev, int reg1, int regn)
+static void 
+ov511_dump_i2c_range(struct usb_ov511 *ov511, int reg1, int regn)
 {
 	int i;
 	int rc;
-	for(i = reg1; i <= regn; i++) {
-		rc = ov511_i2c_read(dev, i);
-		PDEBUG(1, "OV7610[0x%X] = 0x%X", i, rc);
+	for (i = reg1; i <= regn; i++) {
+		rc = ov51x_i2c_read(ov511, i);
+		info("OV7610[0x%X] = 0x%X", i, rc);
 	}
 }
 
-static void ov511_dump_i2c_regs(struct usb_device *dev)
+static void 
+ov51x_dump_i2c_regs(struct usb_ov511 *ov511)
 {
-	PDEBUG(3, "I2C REGS");
-	ov511_dump_i2c_range(dev, 0x00, 0x7C);
+	info("I2C REGS");
+	ov511_dump_i2c_range(ov511, 0x00, 0x7C);
 }
 
-#if 0
-static void ov511_dump_reg_range(struct usb_device *dev, int reg1, int regn)
+static void 
+ov511_dump_reg_range(struct usb_device *dev, int reg1, int regn)
 {
 	int i;
 	int rc;
-	for(i = reg1; i <= regn; i++) {
+	for (i = reg1; i <= regn; i++) {
 	  rc = ov511_reg_read(dev, i);
-	  PDEBUG(1, "OV511[0x%X] = 0x%X", i, rc);
+	  info("OV511[0x%X] = 0x%X", i, rc);
 	}
 }
 
-static void ov511_dump_regs(struct usb_device *dev)
+static void 
+ov511_dump_regs(struct usb_device *dev)
 {
-	PDEBUG(1, "CAMERA INTERFACE REGS");
+	info("CAMERA INTERFACE REGS");
 	ov511_dump_reg_range(dev, 0x10, 0x1f);
-	PDEBUG(1, "DRAM INTERFACE REGS");
+	info("DRAM INTERFACE REGS");
 	ov511_dump_reg_range(dev, 0x20, 0x23);
-	PDEBUG(1, "ISO FIFO REGS");
+	info("ISO FIFO REGS");
 	ov511_dump_reg_range(dev, 0x30, 0x31);
-	PDEBUG(1, "PIO REGS");
+	info("PIO REGS");
 	ov511_dump_reg_range(dev, 0x38, 0x39);
 	ov511_dump_reg_range(dev, 0x3e, 0x3e);
-	PDEBUG(1, "I2C REGS");
+	info("I2C REGS");
 	ov511_dump_reg_range(dev, 0x40, 0x49);
-	PDEBUG(1, "SYSTEM CONTROL REGS");
+	info("SYSTEM CONTROL REGS");
 	ov511_dump_reg_range(dev, 0x50, 0x55);
 	ov511_dump_reg_range(dev, 0x5e, 0x5f);
-	PDEBUG(1, "OmniCE REGS");
+	info("OmniCE REGS");
 	ov511_dump_reg_range(dev, 0x70, 0x79);
+	/* NOTE: Quantization tables are not readable. You will get the value
+	 * in reg. 0x79 for every table register */
 	ov511_dump_reg_range(dev, 0x80, 0x9f);
 	ov511_dump_reg_range(dev, 0xa0, 0xbf);
 
 }
 #endif
-#endif
 
-static int ov511_reset(struct usb_device *dev, unsigned char reset_type)
+/**********************************************************************
+ *
+ * Kernel I2C Interface
+ *
+ **********************************************************************/
+
+/* For as-yet unimplemented I2C interface */
+static void 
+call_i2c_clients(struct usb_ov511 *ov511, unsigned int cmd,
+		 void *arg)
+{
+	/* Do nothing */
+}
+
+/*****************************************************************************/
+
+static int 
+ov511_reset(struct usb_ov511 *ov511, unsigned char reset_type)
 {
 	int rc;
-	
+		
+	/* Setting bit 0 not allowed on 518/518Plus */
+	if (ov511->bridge == BRG_OV518 ||
+	    ov511->bridge == BRG_OV518PLUS)
+		reset_type &= 0xfe;
+
 	PDEBUG(4, "Reset: type=0x%X", reset_type);
-	rc = ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, reset_type);
-	rc = ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0);
+
+	rc = ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET, reset_type);
+	rc = ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET, 0);
 
 	if (rc < 0)
 		err("reset: command failed");
@@ -735,29 +1441,150 @@
 
 /* Temporarily stops OV511 from functioning. Must do this before changing
  * registers while the camera is streaming */
-static inline int ov511_stop(struct usb_device *dev)
+static inline int 
+ov511_stop(struct usb_ov511 *ov511)
 {
 	PDEBUG(4, "stopping");
-	return (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x3d));
+	ov511->stopped = 1;	
+	if (ov511->bridge == BRG_OV518 ||
+	    ov511->bridge == BRG_OV518PLUS)
+		return (ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET,
+					0x3a));
+	else
+		return (ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET,
+					0x3d));
+}
+
+/* Restarts OV511 after ov511_stop() is called. Has no effect if it is not
+ * actually stopped (for performance). */
+static inline int 
+ov511_restart(struct usb_ov511 *ov511)
+{
+	if (ov511->stopped) {
+		PDEBUG(4, "restarting");
+		ov511->stopped = 0;	
+
+		/* Reinitialize the stream */
+		if (ov511->bridge == BRG_OV518 ||
+		    ov511->bridge == BRG_OV518PLUS)
+			ov511_reg_write(ov511->dev, 0x2f, 0x80);
+
+		return (ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET,
+					0x00));
+	}
+
+	return 0;
 }
 
-/* Restarts OV511 after ov511_stop() is called */
-static inline int ov511_restart(struct usb_device *dev)
+/* Resets the hardware snapshot button */
+static void 
+ov51x_clear_snapshot(struct usb_ov511 *ov511)
 {
-	PDEBUG(4, "restarting");
-	return (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x00));
+	if (ov511->bridge == BRG_OV511 || ov511->bridge == BRG_OV511PLUS) {
+		ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01);
+		ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x03);
+		ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01);
+	} else if (ov511->bridge == BRG_OV518 ||
+		   ov511->bridge == BRG_OV518PLUS) {
+		warn("snapshot reset not supported yet on OV518(+)");
+	} else {
+		err("clear snap: invalid bridge type");
+	}
+	
+}
+
+/* Checks the status of the snapshot button. Returns 1 if it was pressed since
+ * it was last cleared, and zero in all other cases (including errors) */
+static int 
+ov51x_check_snapshot(struct usb_ov511 *ov511)
+{
+	int ret, status = 0;
+
+	if (ov511->bridge == BRG_OV511 || ov511->bridge == BRG_OV511PLUS) {
+		ret = ov511_reg_read(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT);
+		if (ret < 0) {
+			err("Error checking snspshot status (%d)", ret);
+		} else if (ret & 0x08) {
+			status = 1;
+		}
+	} else if (ov511->bridge == BRG_OV518 ||
+		   ov511->bridge == BRG_OV518PLUS) {
+		warn("snapshot check not supported yet on OV518(+)");
+	} else {
+		err("check snap: invalid bridge type");
+	}
+
+	return status;
 }
 
-static int ov511_set_packet_size(struct usb_ov511 *ov511, int size)
+/* Sets I2C read and write slave IDs. Returns <0 for error */
+static int 
+ov51x_set_slave_ids(struct usb_ov511 *ov511,
+		    unsigned char write_id,
+		    unsigned char read_id)
 {
-	int alt, mult;
+	struct usb_device *dev = ov511->dev;
 
-	if (ov511_stop(ov511->dev) < 0)
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, write_id) < 0)
 		return -EIO;
 
-	mult = size >> 5;
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, read_id) < 0)
+		return -EIO;
 
-	if (ov511->bridge == BRG_OV511) {
+	if (ov511_reset(ov511, OV511_RESET_NOREGS) < 0)
+		return -EIO;
+
+	return 0;
+}
+
+/* This does an initial reset of an OmniVision sensor and ensures that I2C
+ * is synchronized. Returns <0 for failure.
+ */
+static int 
+ov51x_init_ov_sensor(struct usb_ov511 *ov511)
+{
+	int i, success;
+
+	/* Reset the sensor */ 
+	if (ov51x_i2c_write(ov511, 0x12, 0x80) < 0) return -EIO;
+
+	/* Wait for it to initialize */ 
+	schedule_timeout (1 + 150 * HZ / 1000);
+
+	for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) {
+		if ((ov51x_i2c_read(ov511, OV7610_REG_ID_HIGH) == 0x7F) &&
+		    (ov51x_i2c_read(ov511, OV7610_REG_ID_LOW) == 0xA2)) {
+			success = 1;
+			continue;
+		}
+
+		/* Reset the sensor */ 
+		if (ov51x_i2c_write(ov511, 0x12, 0x80) < 0) return -EIO;
+		/* Wait for it to initialize */ 
+		schedule_timeout(1 + 150 * HZ / 1000);
+		/* Dummy read to sync I2C */
+		if (ov51x_i2c_read(ov511, 0x00) < 0) return -EIO;
+	}
+
+	if (!success)
+		return -EIO;
+	
+	PDEBUG(1, "I2C synced in %d attempt(s)", i);
+
+	return 0;
+}
+
+static int 
+ov511_set_packet_size(struct usb_ov511 *ov511, int size)
+{
+	int alt, mult;
+
+	if (ov511_stop(ov511) < 0)
+		return -EIO;
+
+	mult = size >> 5;
+
+	if (ov511->bridge == BRG_OV511) {
 		if (size == 0) alt = OV511_ALT_SIZE_0;
 		else if (size == 257) alt = OV511_ALT_SIZE_257;
 		else if (size == 513) alt = OV511_ALT_SIZE_513;
@@ -780,6 +1607,20 @@
 			err("Set packet size: invalid size (%d)", size);
 			return -EINVAL;
 		}
+	} else if (ov511->bridge == BRG_OV518 ||
+		   ov511->bridge == BRG_OV518PLUS) {
+		if (size == 0) alt = OV518_ALT_SIZE_0;
+		else if (size == 128) alt = OV518_ALT_SIZE_128;
+		else if (size == 256) alt = OV518_ALT_SIZE_256;
+		else if (size == 384) alt = OV518_ALT_SIZE_384;
+		else if (size == 512) alt = OV518_ALT_SIZE_512;
+		else if (size == 640) alt = OV518_ALT_SIZE_640;
+		else if (size == 768) alt = OV518_ALT_SIZE_768;
+		else if (size == 896) alt = OV518_ALT_SIZE_896;
+		else {
+			err("Set packet size: invalid size (%d)", size);
+			return -EINVAL;
+		}
 	} else {
 		err("Set packet size: Invalid bridge type");
 		return -EINVAL;
@@ -787,351 +1628,1578 @@
 
 	PDEBUG(3, "set packet size: %d, mult=%d, alt=%d", size, mult, alt);
 
-	if (ov511_reg_write(ov511->dev, OV511_REG_FIFO_PACKET_SIZE, mult) < 0)
-		return -ENOMEM;
+	// FIXME: Don't know how to do this on OV518 yet
+	if (ov511->bridge != BRG_OV518 &&
+	    ov511->bridge != BRG_OV518PLUS) {
+		if (ov511_reg_write(ov511->dev, OV511_REG_FIFO_PACKET_SIZE,
+				    mult) < 0) {
+			return -EIO;
+		}
+	}
 	
 	if (usb_set_interface(ov511->dev, ov511->iface, alt) < 0) {
 		err("Set packet size: set interface error");
 		return -EBUSY;
 	}
 
+	/* Initialize the stream */
+	if (ov511->bridge == BRG_OV518 ||
+	    ov511->bridge == BRG_OV518PLUS)
+		if (ov511_reg_write(ov511->dev, 0x2f, 0x80) < 0)
+			return -EIO;
+
 	// FIXME - Should we only reset the FIFO?
-	if (ov511_reset(ov511->dev, OV511_RESET_NOREGS) < 0)
-		return -ENOMEM;
+	if (ov511_reset(ov511, OV511_RESET_NOREGS) < 0)
+		return -EIO;
 
 	ov511->packet_size = size;
 
-	if (ov511_restart(ov511->dev) < 0)
+	if (ov511_restart(ov511) < 0)
 		return -EIO;
 
 	return 0;
 }
 
+/* Upload compression params and quantization tables. Returns 0 for success. */
+static int
+ov511_init_compression(struct usb_ov511 *ov511)
+{
+	struct usb_device *dev = ov511->dev;
+	int rc = 0;
+
+	if (!ov511->compress_inited) {
 
-static inline int
-ov7610_set_picture(struct usb_ov511 *ov511, struct video_picture *p)
+		ov511_reg_write(dev, 0x70, phy);
+		ov511_reg_write(dev, 0x71, phuv);
+		ov511_reg_write(dev, 0x72, pvy);
+		ov511_reg_write(dev, 0x73, pvuv);
+		ov511_reg_write(dev, 0x74, qhy);
+		ov511_reg_write(dev, 0x75, qhuv);
+		ov511_reg_write(dev, 0x76, qvy);
+		ov511_reg_write(dev, 0x77, qvuv);
+
+		if (ov511_upload_quan_tables(dev) < 0) {
+			err("Error uploading quantization tables");
+			rc = -EIO;
+			goto out;
+		}
+	}
+
+	ov511->compress_inited = 1;
+out:	
+	return rc;
+}
+
+/* Upload compression params and quantization tables. Returns 0 for success. */
+static int
+ov518_init_compression(struct usb_ov511 *ov511)
 {
-	int ret;
 	struct usb_device *dev = ov511->dev;
+	int rc = 0;
 
-	PDEBUG(4, "ov511_set_picture");
+	if (!ov511->compress_inited) {
 
-	if (ov511_stop(dev) < 0)
-		return -EIO;
+		if (ov518_upload_quan_tables(dev) < 0) {
+			err("Error uploading quantization tables");
+			rc = -EIO;
+			goto out;
+		}
+	}
 
-	ov511->contrast = p->contrast;
-	ov511->brightness = p->brightness;
-	ov511->colour = p->colour;
-	ov511->hue = p->hue;
-	ov511->whiteness = p->whiteness;
+	ov511->compress_inited = 1;
+out:	
+	return rc;
+}
 
-	if ((ret = ov511_i2c_read(dev, OV7610_REG_COM_B)) < 0)
-		return -EIO;
-#if 0
-	/* disable auto adjust mode */
-	if (ov511_i2c_write(dev, OV7610_REG_COM_B, ret & 0xfe) < 0)
-		return -EIO;
-#endif
-	if (ov511->sensor == SEN_OV7610 || ov511->sensor == SEN_OV7620AE
-		|| ov511->sensor == SEN_OV6620)
-		if (ov511_i2c_write(dev, OV7610_REG_SAT, p->colour >> 8) < 0)
-			return -EIO;
+/* -------------------------------------------------------------------------- */
 
-	if (ov511->sensor == SEN_OV7610 || ov511->sensor == SEN_OV6620) {
-		if (ov511_i2c_write(dev, OV7610_REG_CNT, p->contrast >> 8) < 0)
-			return -EIO;
+/* Sets sensor's contrast setting to "val" */
+static int
+sensor_set_contrast(struct usb_ov511 *ov511, unsigned short val)
+{
+	int rc;
 
-		if (ov511_i2c_write(dev, OV7610_REG_RED, 0xFF - (p->hue >> 8)) < 0)
-			return -EIO;
+	PDEBUG(3, "%d", val);
 
-		if (ov511_i2c_write(dev, OV7610_REG_BLUE, p->hue >> 8) < 0)
+	if (ov511->stop_during_set)
+		if (ov511_stop(ov511) < 0)
 			return -EIO;
 
-		if (ov511_i2c_write(dev, OV7610_REG_BRT, p->brightness >> 8) < 0)
-			return -EIO;
-	} else if ((ov511->sensor == SEN_OV7620) 
-	         || (ov511->sensor == SEN_OV7620AE)) {
-#if 0
-		int cur_sat, new_sat, tmp;
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV6620:
+	case SEN_OV6630:
+	{
+		rc = ov51x_i2c_write(ov511, OV7610_REG_CNT, val >> 8);
+		if (rc < 0)
+			goto out;
+		break;
+	}
+	case SEN_OV7620:
+	{
+		unsigned char ctab[] = {
+			0x01, 0x05, 0x09, 0x11, 0x15, 0x35, 0x37, 0x57,
+			0x5b, 0xa5, 0xa7, 0xc7, 0xc9, 0xcf, 0xef, 0xff
+		};
+
+		/* Use Y gamma control instead. Bit 0 enables it. */
+		rc = ov51x_i2c_write(ov511, 0x64, ctab[val>>12]);
+		if (rc < 0)
+			goto out;
+		break;
+	}
+	case SEN_SAA7111A:
+	{
+		rc = ov51x_i2c_write(ov511, 0x0b, val >> 9);
+		if (rc < 0)
+			goto out;
+		break;
+	}
+	default:
+	{
+		PDEBUG(3, "Unsupported with this sensor");
+		rc = -EPERM;
+		goto out;
+	}
+	}
 
-		cur_sat = ov511_i2c_read(dev, OV7610_REG_BLUE);
+	rc = 0;		/* Success */
+	ov511->contrast = val;
+out:
+	if (ov511_restart(ov511) < 0)
+		return -EIO;
 
-		tmp = (p->hue >> 8) - cur_sat;
-		new_sat = (tmp < 0) ? (-tmp) | 0x80 : tmp;
+	return rc;
+}
 
-	        PDEBUG(1, "cur=%d target=%d diff=%d", cur_sat, p->hue >> 8, tmp); 
+/* Gets sensor's contrast setting */
+static int
+sensor_get_contrast(struct usb_ov511 *ov511, unsigned short *val)
+{
+	int rc;
 
-		if (ov511_i2c_write(dev, OV7610_REG_BLUE, new_sat) < 0)
-			return -EIO;
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV6620:
+	case SEN_OV6630:
+		rc = ov51x_i2c_read(ov511, OV7610_REG_CNT);
+		if (rc < 0)
+			return rc;
+		else
+			*val = rc << 8;
+		break;
+	case SEN_OV7620:
+		/* Use Y gamma reg instead. Bit 0 is the enable bit. */
+		rc = ov51x_i2c_read(ov511, 0x64);
+		if (rc < 0)
+			return rc;
+		else
+			*val = (rc & 0xfe) << 8;
+		break;
+	case SEN_SAA7111A:
+		*val = ov511->contrast;
+		break;
+	default:
+		PDEBUG(3, "Unsupported with this sensor");
+		return -EPERM;
+	}
 
-	        // DEBUG_CODE
-	        PDEBUG(1, "hue=%d", ov511_i2c_read(dev, OV7610_REG_BLUE)); 
+	PDEBUG(3, "%d", *val);
+	ov511->contrast = *val;
 
-#endif
+	return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/* Sets sensor's brightness setting to "val" */
+static int
+sensor_set_brightness(struct usb_ov511 *ov511, unsigned short val)
+{
+	int rc;
+
+	PDEBUG(4, "%d", val);
+
+	if (ov511->stop_during_set)
+		if (ov511_stop(ov511) < 0)
+			return -EIO;
+
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV7620AE:
+	case SEN_OV6620:
+	case SEN_OV6630:
+		rc = ov51x_i2c_write(ov511, OV7610_REG_BRT, val >> 8);
+		if (rc < 0)
+			goto out;
+		break;
+	case SEN_OV7620:
+		/* 7620 doesn't like manual changes when in auto mode */
+		if (!ov511->auto_brt) {
+			rc = ov51x_i2c_write(ov511, OV7610_REG_BRT, val >> 8);
+			if (rc < 0)
+				goto out;
+		}
+		break;
+	case SEN_SAA7111A:
+		rc = ov51x_i2c_write(ov511, 0x0a, val >> 8);
+		if (rc < 0)
+			goto out;
+		break;
+	default:
+		PDEBUG(3, "Unsupported with this sensor");
+		rc = -EPERM;
+		goto out;
 	}
 
-	if (ov511_restart(dev) < 0)
+	rc = 0;		/* Success */
+	ov511->brightness = val;
+out:
+	if (ov511_restart(ov511) < 0)
 		return -EIO;
 
+	return rc;
+}
+
+/* Gets sensor's brightness setting */
+static int
+sensor_get_brightness(struct usb_ov511 *ov511, unsigned short *val)
+{
+	int rc;
+
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV7620AE:
+	case SEN_OV7620:
+	case SEN_OV6620:
+	case SEN_OV6630:
+		rc = ov51x_i2c_read(ov511, OV7610_REG_BRT);
+		if (rc < 0)
+			return rc;
+		else
+			*val = rc << 8;
+		break;
+	case SEN_SAA7111A:
+		*val = ov511->brightness;
+		break;
+	default:
+		PDEBUG(3, "Unsupported with this sensor");
+		return -EPERM;
+	}
+
+	PDEBUG(3, "%d", *val);
+	ov511->brightness = *val;
+
 	return 0;
 }
 
-static inline int
-ov7610_get_picture(struct usb_ov511 *ov511, struct video_picture *p)
+/* -------------------------------------------------------------------------- */
+
+/* Sets sensor's saturation (color intensity) setting to "val" */
+static int
+sensor_set_saturation(struct usb_ov511 *ov511, unsigned short val)
 {
-	int ret;
-	struct usb_device *dev = ov511->dev;
+	int rc;
 
-	PDEBUG(4, "ov511_get_picture");
+	PDEBUG(3, "%d", val);
+
+	if (ov511->stop_during_set)
+		if (ov511_stop(ov511) < 0)
+			return -EIO;
+
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV7620AE:
+	case SEN_OV6620:
+	case SEN_OV6630:
+		rc = ov51x_i2c_write(ov511, OV7610_REG_SAT, val >> 8);
+		if (rc < 0)
+			goto out;
+		break;
+	case SEN_OV7620:
+//		/* Use UV gamma control instead. Bits 0 & 7 are reserved. */
+//		rc = ov511_i2c_write(ov511->dev, 0x62, (val >> 9) & 0x7e);
+//		if (rc < 0)
+//			goto out;
+		rc = ov51x_i2c_write(ov511, OV7610_REG_SAT, val >> 8);
+		if (rc < 0)
+			goto out;
+		break;
+	case SEN_SAA7111A:
+		rc = ov51x_i2c_write(ov511, 0x0c, val >> 9);
+		if (rc < 0)
+			goto out;
+		break;
+	default:
+		PDEBUG(3, "Unsupported with this sensor");
+		rc = -EPERM;
+		goto out;
+	}
 
-	if (ov511_stop(dev) < 0)
+	rc = 0;		/* Success */
+	ov511->colour = val;
+out:
+	if (ov511_restart(ov511) < 0)
 		return -EIO;
 
-	if ((ret = ov511_i2c_read(dev, OV7610_REG_SAT)) < 0) return -EIO;
-	p->colour = ret << 8;
+	return rc;
+}
+
+/* Gets sensor's saturation (color intensity) setting */
+static int
+sensor_get_saturation(struct usb_ov511 *ov511, unsigned short *val)
+{
+	int rc;
+
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV7620AE:
+	case SEN_OV6620:
+	case SEN_OV6630:
+		rc = ov51x_i2c_read(ov511, OV7610_REG_SAT);
+		if (rc < 0)
+			return rc;
+		else
+			*val = rc << 8;
+		break;
+	case SEN_OV7620:
+//		/* Use UV gamma reg instead. Bits 0 & 7 are reserved. */
+//		rc = ov51x_i2c_read(ov511, 0x62);
+//		if (rc < 0)
+//			return rc;
+//		else
+//			*val = (rc & 0x7e) << 9;
+		rc = ov51x_i2c_read(ov511, OV7610_REG_SAT);
+		if (rc < 0)
+			return rc;
+		else
+			*val = rc << 8;
+		break;
+	case SEN_SAA7111A:
+		*val = ov511->colour;
+		break;
+	default:
+		PDEBUG(3, "Unsupported with this sensor");
+		return -EPERM;
+	}
+
+	PDEBUG(3, "%d", *val);
+	ov511->colour = *val;
 
-	if ((ret = ov511_i2c_read(dev, OV7610_REG_CNT)) < 0) return -EIO;
-	p->contrast = ret << 8;
+	return 0;
+}
 
-	if ((ret = ov511_i2c_read(dev, OV7610_REG_BRT)) < 0) return -EIO;
-	p->brightness = ret << 8;
+/* -------------------------------------------------------------------------- */
 
-	/* This may not be the best way to do it */
-	if ((ret = ov511_i2c_read(dev, OV7610_REG_BLUE)) < 0) return -EIO;
-	p->hue = ret << 8;
+/* Sets sensor's hue (red/blue balance) setting to "val" */
+static int
+sensor_set_hue(struct usb_ov511 *ov511, unsigned short val)
+{
+	int rc;
 
-	p->whiteness = 105 << 8;
+	PDEBUG(3, "%d", val);
 
-	/* Can we get these from frame[0]? -claudio? */
-	p->depth = ov511->frame[0].depth;
-	p->palette = ov511->frame[0].format;
+	if (ov511->stop_during_set)
+		if (ov511_stop(ov511) < 0)
+			return -EIO;
+
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV6620:
+	case SEN_OV6630:
+		rc = ov51x_i2c_write(ov511, OV7610_REG_RED, 0xFF - (val >> 8));
+		if (rc < 0)
+			goto out;
+
+		rc = ov51x_i2c_write(ov511, OV7610_REG_BLUE, val >> 8);
+		if (rc < 0)
+			goto out;
+		break;
+	case SEN_OV7620:
+// Hue control is causing problems. I will enable it once it's fixed.
+#if 0
+		rc = ov51x_i2c_write(ov511, 0x7a,
+				     (unsigned char)(val >> 8) + 0xb);
+		if (rc < 0)
+			goto out;
+
+		rc = ov51x_i2c_write(ov511, 0x79, 
+				     (unsigned char)(val >> 8) + 0xb);
+		if (rc < 0)
+			goto out;
+#endif
+		break;
+	case SEN_SAA7111A:
+		rc = ov51x_i2c_write(ov511, 0x0d, (val + 32768) >> 8);
+		if (rc < 0)
+			goto out;
+		break;
+	default:
+		PDEBUG(3, "Unsupported with this sensor");
+		rc = -EPERM;
+		goto out;
+	}
 
-	if (ov511_restart(dev) < 0)
+	rc = 0;		/* Success */
+	ov511->hue = val;
+out:
+	if (ov511_restart(ov511) < 0)
 		return -EIO;
 
+	return rc;
+}
+
+/* Gets sensor's hue (red/blue balance) setting */
+static int
+sensor_get_hue(struct usb_ov511 *ov511, unsigned short *val)
+{
+	int rc;
+
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV6620:
+	case SEN_OV6630:
+		rc = ov51x_i2c_read(ov511, OV7610_REG_BLUE);
+		if (rc < 0)
+			return rc;
+		else
+			*val = rc << 8;
+		break;
+	case SEN_OV7620:
+		rc = ov51x_i2c_read(ov511, 0x7a);
+		if (rc < 0)
+			return rc;
+		else
+			*val = rc << 8;
+		break;
+	case SEN_SAA7111A:
+		*val = ov511->hue;
+		break;
+	default:
+		PDEBUG(3, "Unsupported with this sensor");
+		return -EPERM;
+	}
+
+	PDEBUG(3, "%d", *val);
+	ov511->hue = *val;
+
 	return 0;
 }
 
-/* Returns number of bits per pixel (regardless of where they are located; planar or
- * not), or zero for unsupported format.
- */
-static int ov511_get_depth(int palette)
+/* -------------------------------------------------------------------------- */
+
+static inline int
+sensor_set_picture(struct usb_ov511 *ov511, struct video_picture *p)
 {
-	switch (palette) {
-	case VIDEO_PALETTE_GREY:    return 8;
-	case VIDEO_PALETTE_RGB565:  return 16;
-	case VIDEO_PALETTE_RGB24:   return 24;  
-	case VIDEO_PALETTE_YUV422:  return 16;
-	case VIDEO_PALETTE_YUYV:    return 16;
-	case VIDEO_PALETTE_YUV420:  return 24;
-	case VIDEO_PALETTE_YUV422P: return 24; /* Planar */
-	default:		    return 0;  /* Invalid format */
+	int rc;
+
+	PDEBUG(4, "sensor_set_picture");
+
+	ov511->whiteness = p->whiteness;
+
+	/* Don't return error if a setting is unsupported, or rest of settings
+         * will not be performed */
+
+	rc = sensor_set_contrast(ov511, p->contrast);
+	if (FATAL_ERROR(rc))
+		return rc;
+
+	rc = sensor_set_brightness(ov511, p->brightness);
+	if (FATAL_ERROR(rc))
+		return rc;
+
+	rc = sensor_set_saturation(ov511, p->colour);
+	if (FATAL_ERROR(rc))
+		return rc;
+
+	rc = sensor_set_hue(ov511, p->hue);
+	if (FATAL_ERROR(rc))
+		return rc;
+
+	return 0;
+}
+
+static inline int
+sensor_get_picture(struct usb_ov511 *ov511, struct video_picture *p)
+{
+	int rc;
+
+	PDEBUG(4, "sensor_get_picture");
+
+	/* Don't return error if a setting is unsupported, or rest of settings
+         * will not be performed */
+
+	rc = sensor_get_contrast(ov511, &(p->contrast));
+	if (FATAL_ERROR(rc))
+		return rc;
+
+	rc = sensor_get_brightness(ov511, &(p->brightness));
+	if (FATAL_ERROR(rc))
+		return rc;
+
+	rc = sensor_get_saturation(ov511, &(p->colour));
+	if (FATAL_ERROR(rc))
+		return rc;
+
+	rc = sensor_get_hue(ov511, &(p->hue));
+	if (FATAL_ERROR(rc))
+		return rc;
+
+	p->whiteness = 105 << 8;
+
+	/* Can we get these from frame[0]? -claudio? */
+	p->depth = ov511->frame[0].depth;
+	p->palette = ov511->frame[0].format;
+
+	return 0;
+}
+
+// FIXME: Exposure range is only 0x00-0x7f in interlace mode
+/* Sets current exposure for sensor. This only has an effect if auto-exposure
+ * is off */
+static inline int
+sensor_set_exposure(struct usb_ov511 *ov511, unsigned char val)
+{
+	int rc;
+
+	PDEBUG(3, "%d", val);
+
+	if (ov511->stop_during_set)
+		if (ov511_stop(ov511) < 0)
+			return -EIO;
+
+	switch (ov511->sensor) {
+	case SEN_OV6620:
+	case SEN_OV6630:
+	case SEN_OV7610:
+	case SEN_OV7620:
+	case SEN_OV7620AE:
+	case SEN_OV8600:
+		rc = ov51x_i2c_write(ov511, 0x10, val);
+		if (rc < 0)
+			goto out;
+
+		break;
+	case SEN_KS0127:
+	case SEN_KS0127B:
+	case SEN_SAA7111A:
+		PDEBUG(3, "Unsupported with this sensor");
+		return -EPERM;
+	default:
+		err("Sensor not supported for set_exposure");
+		return -EINVAL;
+	}
+
+	rc = 0;		/* Success */
+	ov511->exposure = val;
+out:
+	if (ov511_restart(ov511) < 0)
+		return -EIO;
+
+	return rc;
+}
+
+/* Gets current exposure level from sensor, regardless of whether it is under
+ * manual control. */
+static int
+sensor_get_exposure(struct usb_ov511 *ov511, unsigned char *val)
+{
+	int rc;
+
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV6620:
+	case SEN_OV6630:
+	case SEN_OV7620:
+	case SEN_OV7620AE:
+	case SEN_OV8600:
+		rc = ov51x_i2c_read(ov511, 0x10);
+		if (rc < 0)
+			return rc;
+		else
+			*val = rc;
+		break;
+	case SEN_KS0127:
+	case SEN_KS0127B:
+	case SEN_SAA7111A:
+		val = 0;
+		PDEBUG(3, "Unsupported with this sensor");
+		return -EPERM;
+	default:
+		err("Sensor not supported for get_exposure");
+		return -EINVAL;
+	}
+
+	PDEBUG(3, "%d", *val);
+	ov511->exposure = *val;
+
+	return 0;
+}
+
+/* Turns on or off the LED. Only has an effect with OV511+/OV518(+) */
+static inline void 
+ov51x_led_control(struct usb_ov511 *ov511, int enable)
+{
+	PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
+
+	if (ov511->bridge == BRG_OV511PLUS)
+		ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_LED_CTL, 
+			        enable ? 1 : 0);
+	else if (ov511->bridge == BRG_OV518 ||
+		 ov511->bridge == BRG_OV518PLUS)
+		ov511_reg_write_mask(ov511->dev, OV518_REG_GPIO_OUT, 
+				     enable ? 0x02 : 0x00, 0x02);
+	return;
+}
+
+/* Matches the sensor's internal frame rate to the lighting frequency.
+ * Valid frequencies are:
+ *	50 - 50Hz, for European and Asian lighting
+ *	60 - 60Hz, for American lighting
+ *
+ * Tested with: OV7610, OV7620, OV7620AE, OV6620
+ * Unsupported: KS0127, KS0127B, SAA7111A
+ * Returns: 0 for success
+ */
+static int
+sensor_set_light_freq(struct usb_ov511 *ov511, int freq)
+{
+	int sixty;
+
+	PDEBUG(4, "%d Hz", freq);
+
+	if (freq == 60)
+		sixty = 1;
+	else if (freq == 50)
+		sixty = 0;
+	else {
+		err("Invalid light freq (%d Hz)", freq);
+		return -EINVAL;
+	}
+
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+		ov51x_i2c_write_mask(ov511, 0x2a, sixty?0x00:0x80, 0x80);
+		ov51x_i2c_write(ov511, 0x2b, sixty?0x00:0xac);
+		ov51x_i2c_write_mask(ov511, 0x13, 0x10, 0x10);
+		ov51x_i2c_write_mask(ov511, 0x13, 0x00, 0x10);
+		break;
+	case SEN_OV7620:
+	case SEN_OV7620AE:
+	case SEN_OV8600:
+		ov51x_i2c_write_mask(ov511, 0x2a, sixty?0x00:0x80, 0x80);
+		ov51x_i2c_write(ov511, 0x2b, sixty?0x00:0xac);
+		ov51x_i2c_write_mask(ov511, 0x76, 0x01, 0x01);
+		break;		
+	case SEN_OV6620:
+	case SEN_OV6630:
+		ov51x_i2c_write(ov511, 0x2b, sixty?0xa8:0x28);
+		ov51x_i2c_write(ov511, 0x2a, sixty?0x84:0xa4);
+		break;
+	case SEN_KS0127:
+	case SEN_KS0127B:
+	case SEN_SAA7111A:
+		PDEBUG(5, "Unsupported with this sensor");
+		return -EPERM;
+	default:
+		err("Sensor not supported for set_light_freq");
+		return -EINVAL;
+	}
+
+	ov511->lightfreq = freq;
+
+	return 0;
+}
+
+/* If enable is true, turn on the sensor's banding filter, otherwise turn it
+ * off. This filter tries to reduce the pattern of horizontal light/dark bands
+ * caused by some (usually fluorescent) lighting. The light frequency must be
+ * set either before or after enabling it with ov51x_set_light_freq().
+ *
+ * Tested with: OV7610, OV7620, OV7620AE, OV6620.
+ * Unsupported: KS0127, KS0127B, SAA7111A
+ * Returns: 0 for success
+ */
+static inline int
+sensor_set_banding_filter(struct usb_ov511 *ov511, int enable)
+{
+	int rc;
+
+	PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
+
+	if (ov511->sensor == SEN_KS0127 || ov511->sensor == SEN_KS0127B
+		|| ov511->sensor == SEN_SAA7111A) {
+		PDEBUG(5, "Unsupported with this sensor");
+		return -EPERM;
+	}
+
+	rc = ov51x_i2c_write_mask(ov511, 0x2d, enable?0x04:0x00, 0x04);
+	if (rc < 0)
+		return rc;
+
+	ov511->bandfilt = enable;
+
+	return 0;
+}
+
+/* If enable is true, turn on the sensor's auto brightness control, otherwise
+ * turn it off.
+ *
+ * Unsupported: KS0127, KS0127B, SAA7111A
+ * Returns: 0 for success
+ */
+static inline int
+sensor_set_auto_brightness(struct usb_ov511 *ov511, int enable)
+{
+	int rc;
+
+	PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
+
+	if (ov511->sensor == SEN_KS0127 || ov511->sensor == SEN_KS0127B
+		|| ov511->sensor == SEN_SAA7111A) {
+		PDEBUG(5, "Unsupported with this sensor");
+		return -EPERM;
+	}
+
+	rc = ov51x_i2c_write_mask(ov511, 0x2d, enable?0x10:0x00, 0x10);
+	if (rc < 0)
+		return rc;
+
+	ov511->auto_brt = enable;
+
+	return 0;
+}
+
+/* If enable is true, turn on the sensor's auto exposure control, otherwise
+ * turn it off.
+ *
+ * Unsupported: KS0127, KS0127B, SAA7111A
+ * Returns: 0 for success
+ */
+static inline int
+sensor_set_auto_exposure(struct usb_ov511 *ov511, int enable)
+{	
+	PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
+
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+		ov51x_i2c_write_mask(ov511, 0x29, enable?0x00:0x80, 0x80);
+		break;
+	case SEN_OV6620:
+	case SEN_OV7620:
+	case SEN_OV7620AE:
+	case SEN_OV8600:
+		ov51x_i2c_write_mask(ov511, 0x13, enable?0x01:0x00, 0x01);
+		break;		
+	case SEN_OV6630:
+		ov51x_i2c_write_mask(ov511, 0x28, enable?0x00:0x10, 0x10);
+		break;
+	case SEN_KS0127:
+	case SEN_KS0127B:
+	case SEN_SAA7111A:
+		PDEBUG(5, "Unsupported with this sensor");
+		return -EPERM;
+	default:
+		err("Sensor not supported for set_auto_exposure");
+		return -EINVAL;
+	}
+
+	ov511->auto_exp = enable;
+
+	return 0;
+}
+
+/* Modifies the sensor's exposure algorithm to allow proper exposure of objects
+ * that are illuminated from behind.
+ *
+ * Tested with: OV6620, OV7620
+ * Unsupported: OV7610, OV7620AE, KS0127, KS0127B, SAA7111A
+ * Returns: 0 for success
+ */
+static int
+sensor_set_backlight(struct usb_ov511 *ov511, int enable)
+{
+
+	PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
+
+	switch (ov511->sensor) {
+	case SEN_OV7620:
+	case SEN_OV8600:
+		ov51x_i2c_write_mask(ov511, 0x68, enable?0xe0:0xc0, 0xe0);
+		ov51x_i2c_write_mask(ov511, 0x29, enable?0x08:0x00, 0x08);
+		ov51x_i2c_write_mask(ov511, 0x28, enable?0x02:0x00, 0x02);
+		break;		
+	case SEN_OV6620:
+		ov51x_i2c_write_mask(ov511, 0x4e, enable?0xe0:0xc0, 0xe0);
+		ov51x_i2c_write_mask(ov511, 0x29, enable?0x08:0x00, 0x08);
+		ov51x_i2c_write_mask(ov511, 0x0e, enable?0x80:0x00, 0x80);
+		break;
+	case SEN_OV6630:
+		ov51x_i2c_write_mask(ov511, 0x4e, enable?0x80:0x60, 0xe0);
+		ov51x_i2c_write_mask(ov511, 0x29, enable?0x08:0x00, 0x08);
+		ov51x_i2c_write_mask(ov511, 0x28, enable?0x02:0x00, 0x02);
+		break;
+	case SEN_OV7610:
+	case SEN_OV7620AE:
+	case SEN_KS0127:
+	case SEN_KS0127B:
+	case SEN_SAA7111A:
+		PDEBUG(5, "Unsupported with this sensor");
+		return -EPERM;
+	default:
+		err("Sensor not supported for set_backlight");
+		return -EINVAL;
+	}
+
+	ov511->backlight = enable;
+
+	return 0;
+}
+
+/* Returns number of bits per pixel (regardless of where they are located;
+ * planar or not), or zero for unsupported format.
+ */
+static inline int 
+ov511_get_depth(int palette)
+{
+	switch (palette) {
+	case VIDEO_PALETTE_GREY:    return 8;
+	case VIDEO_PALETTE_RGB565:  return 16;
+	case VIDEO_PALETTE_RGB24:   return 24;  
+	case VIDEO_PALETTE_YUV422:  return 16;
+	case VIDEO_PALETTE_YUYV:    return 16;
+	case VIDEO_PALETTE_YUV420:  return 12;
+	case VIDEO_PALETTE_YUV422P: return 16; /* Planar */
+	case VIDEO_PALETTE_YUV420P: return 12; /* Planar */
+	default:		    return 0;  /* Invalid format */
+	}
+}
+
+/* Bytes per frame. Used by read(). Return of 0 indicates error */
+static inline long int 
+get_frame_length(struct ov511_frame *frame)
+{
+	if (!frame)
+		return 0;
+	else
+		return ((frame->width * frame->height
+			 * ov511_get_depth(frame->format)) >> 3);
+}
+
+static int
+mode_init_ov_sensor_regs(struct usb_ov511 *ov511, int width, int height,
+			 int mode, int sub_flag, int qvga)
+{
+	int clock;
+
+	/******** Mode (VGA/QVGA) and sensor specific regs ********/
+
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+		ov51x_i2c_write(ov511, 0x14, qvga?0x24:0x04);
+// FIXME: Does this improve the image quality or frame rate?
+#if 0
+		ov51x_i2c_write_mask(ov511, 0x28, qvga?0x00:0x20, 0x20);
+		ov51x_i2c_write(ov511, 0x24, 0x10);
+		ov51x_i2c_write(ov511, 0x25, qvga?0x40:0x8a);
+		ov51x_i2c_write(ov511, 0x2f, qvga?0x30:0xb0);
+		ov51x_i2c_write(ov511, 0x35, qvga?0x1c:0x9c);
+#endif
+		break;
+	case SEN_OV7620:
+//		ov51x_i2c_write(ov511, 0x2b, 0x00);
+		ov51x_i2c_write(ov511, 0x14, qvga?0xa4:0x84);
+		ov51x_i2c_write_mask(ov511, 0x28, qvga?0x00:0x20, 0x20);
+		ov51x_i2c_write(ov511, 0x24, qvga?0x20:0x3a);
+		ov51x_i2c_write(ov511, 0x25, qvga?0x30:0x60);
+		ov51x_i2c_write_mask(ov511, 0x2d, qvga?0x40:0x00, 0x40);
+		ov51x_i2c_write_mask(ov511, 0x67, qvga?0xf0:0x90, 0xf0);
+		ov51x_i2c_write_mask(ov511, 0x74, qvga?0x20:0x00, 0x20);
+		break;
+	case SEN_OV7620AE:
+//		ov51x_i2c_write(ov511, 0x2b, 0x00);
+		ov51x_i2c_write(ov511, 0x14, qvga?0xa4:0x84);
+// FIXME: Enable this once 7620AE uses 7620 initial settings
+#if 0
+		ov51x_i2c_write_mask(ov511, 0x28, qvga?0x00:0x20, 0x20);
+		ov51x_i2c_write(ov511, 0x24, qvga?0x20:0x3a);
+		ov51x_i2c_write(ov511, 0x25, qvga?0x30:0x60);
+		ov51x_i2c_write_mask(ov511, 0x2d, qvga?0x40:0x00, 0x40);
+		ov51x_i2c_write_mask(ov511, 0x67, qvga?0xb0:0x90, 0xf0);
+		ov51x_i2c_write_mask(ov511, 0x74, qvga?0x20:0x00, 0x20);
+#endif
+		break;
+	case SEN_OV6620:
+	case SEN_OV6630:
+		ov51x_i2c_write(ov511, 0x14, qvga?0x24:0x04);
+		/* No special settings yet */
+		break;
+	default:
+		err("Invalid sensor");
+		return -EINVAL;
+	}
+
+	/******** Palette-specific regs ********/
+
+	if (mode == VIDEO_PALETTE_GREY) {
+		if (ov511->sensor == SEN_OV7610
+		    || ov511->sensor == SEN_OV7620AE) {
+			/* these aren't valid on the OV6620/OV7620/6630? */
+			ov51x_i2c_write_mask(ov511, 0x0e, 0x40, 0x40);
+		}
+		ov51x_i2c_write_mask(ov511, 0x13, 0x20, 0x20);
+	} else {
+		if (ov511->sensor == SEN_OV7610
+		    || ov511->sensor == SEN_OV7620AE) {
+			/* not valid on the OV6620/OV7620/6630? */
+			ov51x_i2c_write_mask(ov511, 0x0e, 0x00, 0x40);
+		}
+		ov51x_i2c_write_mask(ov511, 0x13, 0x00, 0x20);
+	}
+
+	/******** Clock programming ********/
+
+	// FIXME: Test this with OV6630
+
+	/* The OV6620 needs special handling. This prevents the 
+	 * severe banding that normally occurs */
+	if (ov511->sensor == SEN_OV6620 || ov511->sensor == SEN_OV6630)
+	{
+		/* Clock down */
+
+		ov51x_i2c_write(ov511, 0x2a, 0x04);
+
+		if (ov511->compress) {
+//			clock = 0;    /* This ensures the highest frame rate */
+			clock = 3;
+		} else if (clockdiv == -1) {   /* If user didn't override it */
+			clock = 3;    /* Gives better exposure time */
+		} else {
+			clock = clockdiv;
+		}
+
+		PDEBUG(4, "Setting clock divisor to %d", clock);
+
+		ov51x_i2c_write(ov511, 0x11, clock);
+
+		ov51x_i2c_write(ov511, 0x2a, 0x84);
+		/* This next setting is critical. It seems to improve
+		 * the gain or the contrast. The "reserved" bits seem
+		 * to have some effect in this case. */
+		ov51x_i2c_write(ov511, 0x2d, 0x85);
+	}
+	else
+	{
+		if (ov511->compress) {
+			clock = 1;    /* This ensures the highest frame rate */
+		} else if (clockdiv == -1) {   /* If user didn't override it */
+			/* Calculate and set the clock divisor */
+			clock = ((sub_flag ? ov511->subw * ov511->subh
+				  : width * height)
+				 * (mode == VIDEO_PALETTE_GREY ? 2 : 3) / 2)
+				 / 66000;
+		} else {
+			clock = clockdiv;
+		}
+
+		PDEBUG(4, "Setting clock divisor to %d", clock);
+
+		ov51x_i2c_write(ov511, 0x11, clock);
+	}
+
+	/******** Special Features ********/
+
+	if (framedrop >= 0)
+		ov51x_i2c_write(ov511, 0x16, framedrop);
+
+	/* We only have code to convert GBR -> RGB24 */
+	if ((mode == VIDEO_PALETTE_RGB24) && sensor_gbr)
+		ov51x_i2c_write_mask(ov511, 0x12, 0x08, 0x08);
+	else
+		ov51x_i2c_write_mask(ov511, 0x12, 0x00, 0x08);
+
+	/* Test Pattern */
+	ov51x_i2c_write_mask(ov511, 0x12, (testpat?0x02:0x00), 0x02);
+
+	/* Auto white balance */
+//	if (awb)
+		ov51x_i2c_write_mask(ov511, 0x12, 0x04, 0x04);
+//	else
+//		ov51x_i2c_write_mask(ov511, 0x12, 0x00, 0x04);
+
+	// This will go away as soon as ov511_mode_init_sensor_regs()
+	// is fully tested.
+	/* 7620/6620/6630? don't have register 0x35, so play it safe */
+	if (ov511->sensor == SEN_OV7610 ||
+	    ov511->sensor == SEN_OV7620AE) {
+		if (width == 640 && height == 480)
+			ov51x_i2c_write(ov511, 0x35, 0x9e);
+		else
+			ov51x_i2c_write(ov511, 0x35, 0x1e);
+	}
+
+	return 0;
+}
+
+static int
+set_ov_sensor_window(struct usb_ov511 *ov511, int width, int height, int mode,
+		     int sub_flag)
+{
+	int ret;
+	int hwsbase, hwebase, vwsbase, vwebase, hwsize, vwsize; 
+	int hoffset, voffset, hwscale = 0, vwscale = 0;
+
+	/* The different sensor ICs handle setting up of window differently.
+	 * IF YOU SET IT WRONG, YOU WILL GET ALL ZERO ISOC DATA FROM OV51x!!! */
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV7620AE:
+		hwsbase = 0x38;
+		hwebase = 0x3a;
+		vwsbase = vwebase = 0x05;
+		break;
+	case SEN_OV6620:
+	case SEN_OV6630:	// FIXME: Is this right?
+		hwsbase = 0x38;
+		hwebase = 0x3a;
+		vwsbase = 0x05;
+		vwebase = 0x06;
+		break;
+	case SEN_OV7620:
+		hwsbase = 0x2f;		/* From 7620.SET (spec is wrong) */
+		hwebase = 0x2f;
+		vwsbase = vwebase = 0x05;
+		break;
+	default:
+		err("Invalid sensor");
+		return -EINVAL;
+	}
+
+	if (ov511->sensor == SEN_OV6620 || ov511->sensor == SEN_OV6630) {
+		if (width > 176 && height > 144) {  /* CIF */
+			ret = mode_init_ov_sensor_regs(ov511, width, height,
+				mode, sub_flag, 0);
+			if (ret < 0)
+				return ret;
+			hwscale = 1;
+			vwscale = 1;  /* The datasheet says 0; it's wrong */
+			hwsize = 352;
+			vwsize = 288;
+		} else if (width > 176 || height > 144) {
+			err("Illegal dimensions");
+			return -EINVAL;
+		} else {			    /* QCIF */
+			ret = mode_init_ov_sensor_regs(ov511, width, height,
+				mode, sub_flag, 1);
+			if (ret < 0)
+				return ret;
+			hwsize = 176;
+			vwsize = 144;
+		}
+	} else {
+		if (width > 320 && height > 240) {  /* VGA */
+			ret = mode_init_ov_sensor_regs(ov511, width, height,
+				mode, sub_flag, 0);
+			if (ret < 0)
+				return ret;
+			hwscale = 2;
+			vwscale = 1;
+			hwsize = 640;
+			vwsize = 480;
+		} else if (width > 320 || height > 240) {
+			err("Illegal dimensions");
+			return -EINVAL;
+		} else {			    /* QVGA */
+			ret = mode_init_ov_sensor_regs(ov511, width, height,
+				mode, sub_flag, 1);
+			if (ret < 0)
+				return ret;
+			hwscale = 1;
+			hwsize = 320;
+			vwsize = 240;
+		}
+	}
+
+	/* Center the window */
+	hoffset = ((hwsize - width) / 2) >> hwscale;
+	voffset = ((vwsize - height) / 2) >> vwscale;
+
+	/* FIXME! - This needs to be changed to support 160x120 and 6620!!! */
+	if (sub_flag) {
+		ov51x_i2c_write(ov511, 0x17, hwsbase+(ov511->subx>>hwscale));
+		ov51x_i2c_write(ov511, 0x18,
+				hwebase+((ov511->subx+ov511->subw)>>hwscale));
+		ov51x_i2c_write(ov511, 0x19, vwsbase+(ov511->suby>>vwscale));
+		ov51x_i2c_write(ov511, 0x1a,
+				vwebase+((ov511->suby+ov511->subh)>>vwscale));
+	} else {
+		ov51x_i2c_write(ov511, 0x17, hwsbase + hoffset);
+		ov51x_i2c_write(ov511, 0x18,
+				hwebase + hoffset + (hwsize>>hwscale));
+		ov51x_i2c_write(ov511, 0x19, vwsbase + voffset);
+		ov51x_i2c_write(ov511, 0x1a,
+				vwebase + voffset + (vwsize>>vwscale));
+	}
+
+#ifdef OV511_DEBUG
+	if (dump_sensor)
+		ov51x_dump_i2c_regs(ov511);
+#endif
+
+	return 0;
+}
+
+/* Set up the OV511/OV511+ with the given image parameters.
+ *
+ * Do not put any sensor-specific code in here (including I2C I/O functions)
+ */
+static int
+ov511_mode_init_regs(struct usb_ov511 *ov511,
+		     int width, int height, int mode, int sub_flag)
+{
+	int lncnt, pxcnt, rc = 0;
+	struct usb_device *dev = ov511->dev;
+
+	if (!ov511 || !dev)
+		return -EFAULT;
+
+	if (sub_flag) {
+		width = ov511->subw;
+		height = ov511->subh;
+	}
+
+	PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d",
+	       width, height, mode, sub_flag);
+
+	// FIXME: This should be moved to a 7111a-specific function once
+	// subcapture is dealt with properly
+	if (ov511->sensor == SEN_SAA7111A) {
+		if (width == 320 && height == 240) {
+			/* No need to do anything special */
+		} else if (width == 640 && height == 480) {
+			/* Set the OV511 up as 320x480, but keep the V4L
+			 * resolution as 640x480 */
+			width = 320;
+		} else {
+			err("SAA7111A only supports 320x240 or 640x480");
+			return -EINVAL;
+		}
+	}
+
+	/* Make sure width and height are a multiple of 8 */
+	if (width % 8 || height % 8) {
+		err("Invalid size (%d, %d) (mode = %d)", width, height, mode);
+		return -EINVAL;
+	}
+
+	if (width < ov511->minwidth || height < ov511->minheight) {
+		err("Requested dimensions are too small");
+		return -EINVAL;
+	}
+
+	if (ov511_stop(ov511) < 0)
+		return -EIO;
+
+	if (mode == VIDEO_PALETTE_GREY) {
+		ov511_reg_write(dev, 0x16, 0x00);
+
+		/* For snapshot */
+		ov511_reg_write(dev, 0x1e, 0x00);
+		ov511_reg_write(dev, 0x1f, 0x01);
+	} else {
+		ov511_reg_write(dev, 0x16, 0x01);
+
+		/* For snapshot */
+		ov511_reg_write(dev, 0x1e, 0x01);
+		ov511_reg_write(dev, 0x1f, 0x03);
+	}
+
+	/* Here I'm assuming that snapshot size == image size.
+	 * I hope that's always true. --claudio
+	 */
+	pxcnt = (width >> 3) - 1;
+	lncnt = (height >> 3) - 1;
+
+	ov511_reg_write(dev, 0x12, pxcnt);
+	ov511_reg_write(dev, 0x13, lncnt);
+	ov511_reg_write(dev, 0x14, 0x00);
+	ov511_reg_write(dev, 0x15, 0x00);
+	ov511_reg_write(dev, 0x18, 0x03);	/* YUV420, low pass filer on */
+
+	/* Snapshot additions */
+	ov511_reg_write(dev, 0x1a, pxcnt);
+	ov511_reg_write(dev, 0x1b, lncnt);
+        ov511_reg_write(dev, 0x1c, 0x00);
+        ov511_reg_write(dev, 0x1d, 0x00);
+
+	if (ov511->compress) {
+		ov511_reg_write(dev, 0x78, 0x07); // Turn on Y & UV compression
+		ov511_reg_write(dev, 0x79, 0x03); // Enable LUTs
+		ov511_reset(ov511, OV511_RESET_OMNICE);
 	}
+//out:
+	if (ov511_restart(ov511) < 0)
+		return -EIO;
+
+	return rc;
 }
 
-/* LNCNT values fixed by Lawrence Glaister <lg@jfm.bc.ca> */
-static struct mode_list mlist[] = {
-	/* W    H   C  PXCNT LNCNT PXDIV LNDIV M420  COMA  COML */
-	{ 640, 480, 0, 0x4f, 0x3b, 0x00, 0x00, 0x03, 0x24, 0x9e },
-	{ 640, 480, 1, 0x4f, 0x3b, 0x00, 0x00, 0x03, 0x24, 0x9e },
-	{ 320, 240, 0, 0x27, 0x1d, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 320, 240, 1, 0x27, 0x1d, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 352, 288, 0, 0x2b, 0x25, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 352, 288, 1, 0x2b, 0x25, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 384, 288, 0, 0x2f, 0x25, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 384, 288, 1, 0x2f, 0x25, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 448, 336, 0, 0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 448, 336, 1, 0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 176, 144, 0, 0x15, 0x12, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 176, 144, 1, 0x15, 0x12, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 160, 120, 0, 0x13, 0x0e, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 160, 120, 1, 0x13, 0x0e, 0x00, 0x00, 0x03, 0x04, 0x1e },
+static struct mode_list_518 mlist518[] = {
+	/* W    H   reg28 reg29 reg2a reg2c reg2e reg24 reg25 */
+	{ 352, 288, 0x00, 0x16, 0x48, 0x00, 0x00, 0x9f, 0x90 },
+	{ 320, 240, 0x00, 0x14, 0x3c, 0x10, 0x18, 0x9f, 0x90 },
+	{ 176, 144, 0x05, 0x0b, 0x24, 0x00, 0x00, 0xff, 0xf0 },
+	{ 160, 120, 0x05, 0x0a, 0x1e, 0x08, 0x0c, 0xff, 0xf0 },
 	{ 0, 0 }
 };
 
+/* Sets up the OV518/OV518+ with the given image parameters
+ *
+ * OV518 needs a completely different approach, until we can figure out what
+ * the individual registers do. Many register ops are commented out until we
+ * can find out if they are still valid. Also, only 15 FPS is supported now.
+ *
+ * Do not put any sensor-specific code in here (including I2C I/O functions)
+ */
 static int
-ov511_mode_init_regs(struct usb_ov511 *ov511,
+ov518_mode_init_regs(struct usb_ov511 *ov511,
 		     int width, int height, int mode, int sub_flag)
 {
 	int i;
 	struct usb_device *dev = ov511->dev;
-	int hwsbase, hwebase, vwsbase, vwebase, hwsize, vwsize; 
-	int hwscale = 0, vwscale = 0;
+	unsigned char b[3]; /* Multiple-value reg buffer */
 
 	PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d",
 	       width, height, mode, sub_flag);
 
-	if (ov511_stop(ov511->dev) < 0)
+	if (ov511_stop(ov511) < 0)
+		return -EIO;
+
+	for (i = 0; mlist518[i].width; i++) {
+//		int lncnt, pxcnt;
+
+		if (width != mlist518[i].width || height != mlist518[i].height)
+			continue;
+
+// FIXME: Subcapture won't be possible until we know what the registers do
+// FIXME: We can't handle anything but YUV420 so far
+
+//		/* Here I'm assuming that snapshot size == image size.
+//		 * I hope that's always true. --claudio
+//		 */
+//		pxcnt = sub_flag ? (ov511->subw >> 3) - 1 : mlist[i].pxcnt;
+//		lncnt = sub_flag ? (ov511->subh >> 3) - 1 : mlist[i].lncnt;
+//
+//		ov511_reg_write(dev, 0x12, pxcnt);
+//		ov511_reg_write(dev, 0x13, lncnt);
+
+		/******** Set the mode ********/		
+
+		/* Mode independent regs */
+		ov511_reg_write(dev, 0x2b, 0x00);
+		ov511_reg_write(dev, 0x2d, 0x00);
+		ov511_reg_write(dev, 0x3b, 0x00);
+		ov511_reg_write(dev, 0x3d, 0x00);
+
+		/* Mode dependent regs. Regs 38 - 3e are always the same as
+		 * regs 28 - 2e */
+		ov511_reg_write_mask(dev, 0x28, mlist518[i].reg28
+			| (mode == VIDEO_PALETTE_GREY) ? 0x80:0x00, 0x8f);
+		ov511_reg_write(dev, 0x29, mlist518[i].reg29);
+		ov511_reg_write(dev, 0x2a, mlist518[i].reg2a);
+		ov511_reg_write(dev, 0x2c, mlist518[i].reg2c);
+		ov511_reg_write(dev, 0x2e, mlist518[i].reg2e);
+		ov511_reg_write_mask(dev, 0x38, mlist518[i].reg28 
+			| (mode == VIDEO_PALETTE_GREY) ? 0x80:0x00, 0x8f);
+		ov511_reg_write(dev, 0x39, mlist518[i].reg29);
+		ov511_reg_write(dev, 0x3a, mlist518[i].reg2a);
+		ov511_reg_write(dev, 0x3c, mlist518[i].reg2c);
+		ov511_reg_write(dev, 0x3e, mlist518[i].reg2e);
+		ov511_reg_write(dev, 0x24, mlist518[i].reg24);
+		ov511_reg_write(dev, 0x25, mlist518[i].reg25);
+
+		/* Windows driver does this here; who knows why */
+		ov511_reg_write(dev, 0x2f, 0x80);
+
+		/******** Set the framerate (to 15 FPS) ********/		
+
+		/* Mode independent, but framerate dependent, regs */
+		/* These are for 15 FPS only */
+		ov511_reg_write(dev, 0x51, 0x08);
+		ov511_reg_write(dev, 0x22, 0x18);
+		ov511_reg_write(dev, 0x23, 0xff);
+		ov511_reg_write(dev, 0x71, 0x19);  /* Compression-related? */
+
+		// FIXME: Sensor-specific
+		/* Bit 5 is what matters here. Of course, it is "reserved" */
+		ov51x_i2c_write(ov511, 0x54, 0x23);
+
+		ov511_reg_write(dev, 0x2f, 0x80);
+
+		/* Mode dependent regs */
+		if ((width == 352 && height == 288) ||
+		    (width == 320 && height == 240)) {
+			b[0]=0x80; b[1]=0x02;
+			ov518_reg_write_multi(dev, 0x30, b, 2);
+			b[0]=0x90; b[1]=0x01;
+			ov518_reg_write_multi(dev, 0xc4, b, 2);
+			b[0]=0xf4; b[1]=0x01;
+			ov518_reg_write_multi(dev, 0xc6, b, 2);
+			b[0]=0xf4; b[1]=0x01;
+			ov518_reg_write_multi(dev, 0xc7, b, 2);
+			b[0]=0x8e; b[1]=0x00;
+			ov518_reg_write_multi(dev, 0xc8, b, 2);
+			b[0]=0x1a; b[1]=0x00; b[2]=0x02;
+			ov518_reg_write_multi(dev, 0xca, b, 3);
+			b[0]=0x14; b[1]=0x02;
+			ov518_reg_write_multi(dev, 0xcb, b, 2);
+			b[0]=0xd0; b[1]=0x07;
+			ov518_reg_write_multi(dev, 0xcc, b, 2);
+			b[0]=0x20; b[1]=0x00;
+			ov518_reg_write_multi(dev, 0xcd, b, 2);
+			b[0]=0x60; b[1]=0x02;
+			ov518_reg_write_multi(dev, 0xce, b, 2);
+
+		} else if ((width == 176 && height == 144) ||
+			   (width == 160 && height == 120)) {
+			b[0]=0x80; b[1]=0x01;
+			ov518_reg_write_multi(dev, 0x30, b, 2);
+			b[0]=0xc8; b[1]=0x00;
+			ov518_reg_write_multi(dev, 0xc4, b, 2);
+			b[0]=0x40; b[1]=0x01;
+			ov518_reg_write_multi(dev, 0xc6, b, 2);
+			b[0]=0x40; b[1]=0x01;
+			ov518_reg_write_multi(dev, 0xc7, b, 2);
+			b[0]=0x60; b[1]=0x00;
+			ov518_reg_write_multi(dev, 0xc8, b, 2);
+			b[0]=0x0f; b[1]=0x33; b[2]=0x01;
+			ov518_reg_write_multi(dev, 0xca, b, 3);
+			b[0]=0x40; b[1]=0x01;
+			ov518_reg_write_multi(dev, 0xcb, b, 2);
+			b[0]=0xec; b[1]=0x04;
+			ov518_reg_write_multi(dev, 0xcc, b, 2);
+			b[0]=0x13; b[1]=0x00;
+			ov518_reg_write_multi(dev, 0xcd, b, 2);
+			b[0]=0x6d; b[1]=0x01;
+			ov518_reg_write_multi(dev, 0xce, b, 2);
+		} else {
+			/* Can't happen, since we already handled this case */
+			err("ov518_mode_init_regs(): **** logic error ****");
+		}
+
+		ov511_reg_write(dev, 0x2f, 0x80);
+
+		break;
+	}
+
+	if (ov511_restart(ov511) < 0)
+		return -EIO;
+
+	/* Reset it just for good measure */
+	if (ov511_reset(ov511, OV511_RESET_NOREGS) < 0)
 		return -EIO;
 
-	/* Dumppix only works with RGB24 */
-	if (dumppix && (mode != VIDEO_PALETTE_RGB24)) {
-		err("dumppix only supported with RGB 24");
+	if (mlist518[i].width == 0) {
+		err("Unknown mode (%d, %d): %d", width, height, mode);
 		return -EINVAL;
 	}
 
-	if (mode == VIDEO_PALETTE_GREY) {
-		ov511_reg_write(dev, 0x16, 0x00);
-		if (ov511->sensor == SEN_OV7610
-		    || ov511->sensor == SEN_OV7620AE) {
-			/* these aren't valid on the OV6620/OV7620 */
-			ov511_i2c_write(dev, 0x0e, 0x44);
-		}
-		ov511_i2c_write(dev, 0x13, autoadjust ? 0x21 : 0x20);
+	return 0;
+}
 
-		/* For snapshot */
-		ov511_reg_write(dev, 0x1e, 0x00);
-		ov511_reg_write(dev, 0x1f, 0x01);
-	} else {
-		ov511_reg_write(dev, 0x16, 0x01);
-		if (ov511->sensor == SEN_OV7610
-		    || ov511->sensor == SEN_OV7620AE) {
-			/* not valid on the OV6620/OV7620 */
-			ov511_i2c_write(dev, 0x0e, 0x04);
-		}
-		ov511_i2c_write(dev, 0x13, autoadjust ? 0x01 : 0x00);
+/* This is a wrapper around the OV511, OV518, and sensor specific functions */
+static int
+mode_init_regs(struct usb_ov511 *ov511,
+	       int width, int height, int mode, int sub_flag)
+{
+	int rc = 0;
 
-		/* For snapshot */
-		ov511_reg_write(dev, 0x1e, 0x01);
-		ov511_reg_write(dev, 0x1f, 0x03);
+	if (ov511->bridge == BRG_OV518 ||
+	    ov511->bridge == BRG_OV518PLUS) {
+		rc = ov518_mode_init_regs(ov511, width, height, mode, sub_flag);
+	} else {
+		rc = ov511_mode_init_regs(ov511, width, height, mode, sub_flag);
 	}
 
-	/* The different sensor ICs handle setting up of window differently */
+	if (FATAL_ERROR(rc))
+		return rc;
+
 	switch (ov511->sensor) {
 	case SEN_OV7610:
+	case SEN_OV7620:
 	case SEN_OV7620AE:
-		hwsbase = 0x38;
-		hwebase = 0x3a;
-		vwsbase = vwebase = 0x05;
-		break;
+	case SEN_OV8600:
 	case SEN_OV6620:
-		hwsbase = 0x38;
-		hwebase = 0x3a;
-		vwsbase = 0x05;
-		vwebase = 0x06;
+	case SEN_OV6630:
+		rc = set_ov_sensor_window(ov511, width, height, mode, sub_flag);
 		break;
-	case SEN_OV7620:
-		hwsbase = 0x2c;
-		hwebase = 0x2d;
-		vwsbase = vwebase = 0x05;
+	case SEN_KS0127:
+	case SEN_KS0127B:
+		err("KS0127-series decoders not supported yet");
+		rc = -EINVAL;
+		break;
+	case SEN_SAA7111A:
+//		rc = mode_init_saa_sensor_regs(ov511, width, height, mode, 
+//					       sub_flag);
+
+		PDEBUG(1, "SAA status = 0X%x", ov51x_i2c_read(ov511, 0x1f));
 		break;
 	default:
-		err("Invalid sensor");
-		return -EINVAL;
+		err("Unknown sensor");
+		rc = -EINVAL;
 	}
 
-	/* Bit 5 of COM C register varies with sensor */ 
-	if (ov511->sensor == SEN_OV6620) {
-		if (width > 176 && height > 144) {  /* CIF */
-			ov511_i2c_write(dev, 0x14, 0x04);
-			hwscale = 1;
-			vwscale = 1;  /* The datasheet says 0; it's wrong */
-			hwsize = 352;
-			vwsize = 288;
-		} else {			    /* QCIF */
-			ov511_i2c_write(dev, 0x14, 0x24);
-			hwsize = 176;
-			vwsize = 144;
-		}
-	}
-	else {
-		if (width > 320 && height > 240) {  /* VGA */
-			ov511_i2c_write(dev, 0x14, 0x04);
-			hwscale = 2;
-			vwscale = 1;
-			hwsize = 640;
-			vwsize = 480;
-		} else {			    /* QVGA */
-			ov511_i2c_write(dev, 0x14, 0x24);
-			hwscale = 1;
-			hwsize = 320;
-			vwsize = 240;
-		}	
-	}
+	if (FATAL_ERROR(rc))
+		return rc;
 
-	/* FIXME! - This needs to be changed to support 160x120 and 6620!!! */
-	if (sub_flag) {
-		ov511_i2c_write(dev, 0x17, hwsbase+(ov511->subx>>hwscale));
-		ov511_i2c_write(dev, 0x18, hwebase+((ov511->subx+ov511->subw)>>hwscale));
-		ov511_i2c_write(dev, 0x19, vwsbase+(ov511->suby>>vwscale));
-		ov511_i2c_write(dev, 0x1a, vwebase+((ov511->suby+ov511->subh)>>vwscale));
-	} else {
-		ov511_i2c_write(dev, 0x17, hwsbase);
-		ov511_i2c_write(dev, 0x18, hwebase + (hwsize>>hwscale));
-		ov511_i2c_write(dev, 0x19, vwsbase);
-		ov511_i2c_write(dev, 0x1a, vwebase + (vwsize>>vwscale));
-	}
+	/* Sensor-independent settings */
+	rc = sensor_set_auto_brightness(ov511, ov511->auto_brt);
+	if (FATAL_ERROR(rc))
+		return rc;
 
-	for (i = 0; mlist[i].width; i++) {
-		int lncnt, pxcnt, clock;
+	rc = sensor_set_auto_exposure(ov511, ov511->auto_exp);
+	if (FATAL_ERROR(rc))
+		return rc;
 
-		if (width != mlist[i].width || height != mlist[i].height)
-			continue;
+	rc = sensor_set_banding_filter(ov511, bandingfilter);
+	if (FATAL_ERROR(rc))
+		return rc;
 
-		if (!mlist[i].color && mode != VIDEO_PALETTE_GREY)
-			continue;
+	if (ov511->lightfreq) {
+		rc = sensor_set_light_freq(ov511, lightfreq);
+		if (FATAL_ERROR(rc))
+			return rc;
+	}
 
-		/* Here I'm assuming that snapshot size == image size.
-		 * I hope that's always true. --claudio
-		 */
-		pxcnt = sub_flag ? (ov511->subw >> 3) - 1 : mlist[i].pxcnt;
-		lncnt = sub_flag ? (ov511->subh >> 3) - 1 : mlist[i].lncnt;
+	rc = sensor_set_backlight(ov511, ov511->backlight);
+	if (FATAL_ERROR(rc))
+		return rc;
 
-		ov511_reg_write(dev, 0x12, pxcnt);
-		ov511_reg_write(dev, 0x13, lncnt);
-		ov511_reg_write(dev, 0x14, mlist[i].pxdv);
-		ov511_reg_write(dev, 0x15, mlist[i].lndv);
-		ov511_reg_write(dev, 0x18, mlist[i].m420);
-
-		/* Snapshot additions */
-		ov511_reg_write(dev, 0x1a, pxcnt);
-		ov511_reg_write(dev, 0x1b, lncnt);
-                ov511_reg_write(dev, 0x1c, mlist[i].pxdv);
-                ov511_reg_write(dev, 0x1d, mlist[i].lndv);
-
-		/* Calculate and set the clock divisor */
-		clock = ((sub_flag ? ov511->subw * ov511->subh : width * height)
-			* (mlist[i].color ? 3 : 2) / 2) / 66000;
-#if 0
-		clock *= cams;
-#endif
-		ov511_i2c_write(dev, 0x11, clock);
+	return 0;
+}
+
+/* This sets the default image parameters (Size = max, RGB24). This is
+ * useful for apps that use read() and do not set these.
+ */
+static int 
+ov51x_set_default_params(struct usb_ov511 *ov511)
+{
+	int i;
+
+	PDEBUG(3, "%dx%d, RGB24", ov511->maxwidth, ov511->maxheight);
 
-		/* We only have code to convert GBR -> RGB24 */
-		if ((mode == VIDEO_PALETTE_RGB24) && sensor_gbr)
-			ov511_i2c_write(dev, 0x12, mlist[i].common_A | (testpat?0x0a:0x08));
+	/* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used
+	 * (using read() instead). */
+	for (i = 0; i < OV511_NUMFRAMES; i++) {
+		ov511->frame[i].width = ov511->maxwidth;
+		ov511->frame[i].height = ov511->maxheight;
+		ov511->frame[i].bytes_read = 0;
+		if (force_palette)
+			ov511->frame[i].format = force_palette;
 		else
-			ov511_i2c_write(dev, 0x12, mlist[i].common_A | (testpat?0x02:0x00));
+			ov511->frame[i].format = VIDEO_PALETTE_RGB24;
+		ov511->frame[i].depth = ov511_get_depth(ov511->frame[i].format);
+	}
+
+	/* Initialize to max width/height, RGB24 */
+	if (mode_init_regs(ov511, ov511->maxwidth, ov511->maxheight,
+			   ov511->frame[0].format, 0) < 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+/**********************************************************************
+ *
+ * Video decoder stuff
+ *
+ **********************************************************************/
 
-		/* 7620/6620 don't have register 0x35, so play it safe */
-		if (ov511->sensor == SEN_OV7610 ||
-		    ov511->sensor == SEN_OV7620AE)
-			ov511_i2c_write(dev, 0x35, mlist[i].common_L);
+/* Set analog input port of decoder */
+static int 
+decoder_set_input(struct usb_ov511 *ov511, int input)
+{
+	PDEBUG(4, "port %d", input);
 
+	switch (ov511->sensor) {
+	case SEN_SAA7111A:
+	{
+		/* Select mode */
+		ov51x_i2c_write_mask(ov511, 0x02, input, 0x07);
+		/* Bypass chrominance trap for modes 4..7 */
+		ov51x_i2c_write_mask(ov511, 0x09,
+				     (input > 3) ? 0x80:0x00, 0x80);
 		break;
 	}
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* Get ASCII name of video input */
+static int 
+decoder_get_input_name(struct usb_ov511 *ov511, int input, char *name)
+{
+	switch (ov511->sensor) {
+	case SEN_SAA7111A:
+	{
+		if (input < 0 || input > 7)
+			return -EINVAL;
+		else if (input < 4)
+			sprintf(name, "CVBS-%d", input);
+		else // if (input < 8)
+			sprintf(name, "S-Video-%d", input - 4);
 
-	if (compress) {
-		ov511_reg_write(dev, 0x78, 0x03); // Turn on Y compression
-		ov511_reg_write(dev, 0x79, 0x00); // Disable LUTs
+		break;
+	}
+	default:
+		sprintf(name, "%s", "Camera");
 	}
 
-	if (ov511_restart(ov511->dev) < 0)
-		return -EIO;
+	return 0;
+}
 
-	if (mlist[i].width == 0) {
-		err("Unknown mode (%d, %d): %d", width, height, mode);
+/* Set norm (NTSC, PAL, SECAM, AUTO) */
+static int 
+decoder_set_norm(struct usb_ov511 *ov511, int norm)
+{
+	PDEBUG(4, "%d", norm);
+
+	switch (ov511->sensor) {
+	case SEN_SAA7111A:
+	{
+		int reg_8, reg_e;
+
+		if (norm == VIDEO_MODE_NTSC) {
+			reg_8 = 0x40;	/* 60 Hz */
+			reg_e = 0x00;	/* NTSC M / PAL BGHI */
+		} else if (norm == VIDEO_MODE_PAL) {
+			reg_8 = 0x00;	/* 50 Hz */
+			reg_e = 0x00;	/* NTSC M / PAL BGHI */	
+		} else if (norm == VIDEO_MODE_AUTO) {
+			reg_8 = 0x80;	/* Auto field detect */
+			reg_e = 0x00;	/* NTSC M / PAL BGHI */
+		} else if (norm == VIDEO_MODE_SECAM) {
+			reg_8 = 0x00;	/* 50 Hz */
+			reg_e = 0x50;	/* SECAM / PAL 4.43 */
+		} else {
+			return -EINVAL;
+		}
+
+		ov51x_i2c_write_mask(ov511, 0x08, reg_8, 0xc0);
+		ov51x_i2c_write_mask(ov511, 0x0e, reg_e, 0x70);
+		break;
+	}
+	default:
 		return -EINVAL;
 	}
 
-#ifdef OV511_DEBUG
-	if (debug >= 5)
-		ov511_dump_i2c_regs(dev);
-#endif
-
 	return 0;
 }
 
+
 /**********************************************************************
  *
  * Color correction functions
@@ -1169,7 +3237,7 @@
 
 static inline void
 ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v, 
-	int rowPixels, unsigned char * rgb, int bits)
+		     int rowPixels, unsigned char * rgb, int bits)
 {
 	const int rvScale = 91881;
 	const int guScale = -22553;
@@ -1192,34 +3260,104 @@
 
 	if (bits == 24) {
 		/* Write out top two pixels */
-		rgb[0] = LIMIT(b+yTL); rgb[1] = LIMIT(g+yTL); rgb[2] = LIMIT(r+yTL);
-		rgb[3] = LIMIT(b+yTR); rgb[4] = LIMIT(g+yTR); rgb[5] = LIMIT(r+yTR);
+		rgb[0] = LIMIT(b+yTL); rgb[1] = LIMIT(g+yTL);
+		rgb[2] = LIMIT(r+yTL);
+
+		rgb[3] = LIMIT(b+yTR); rgb[4] = LIMIT(g+yTR);
+		rgb[5] = LIMIT(r+yTR);
 
 		/* Skip down to next line to write out bottom two pixels */
 		rgb += 3 * rowPixels;
-		rgb[0] = LIMIT(b+yBL); rgb[1] = LIMIT(g+yBL); rgb[2] = LIMIT(r+yBL);
-		rgb[3] = LIMIT(b+yBR); rgb[4] = LIMIT(g+yBR); rgb[5] = LIMIT(r+yBR);
+		rgb[0] = LIMIT(b+yBL); rgb[1] = LIMIT(g+yBL);
+		rgb[2] = LIMIT(r+yBL);
+
+		rgb[3] = LIMIT(b+yBR); rgb[4] = LIMIT(g+yBR);
+		rgb[5] = LIMIT(r+yBR);
 	} else if (bits == 16) {
 		/* Write out top two pixels */
-		rgb[0] = ((LIMIT(b+yTL) >> 3) & 0x1F) | ((LIMIT(g+yTL) << 3) & 0xE0);
-		rgb[1] = ((LIMIT(g+yTL) >> 5) & 0x07) | (LIMIT(r+yTL) & 0xF8);
-
-		rgb[2] = ((LIMIT(b+yTR) >> 3) & 0x1F) | ((LIMIT(g+yTR) << 3) & 0xE0);
-		rgb[3] = ((LIMIT(g+yTR) >> 5) & 0x07) | (LIMIT(r+yTR) & 0xF8);
+		rgb[0] = ((LIMIT(b+yTL) >> 3) & 0x1F) 
+			| ((LIMIT(g+yTL) << 3) & 0xE0);
+		rgb[1] = ((LIMIT(g+yTL) >> 5) & 0x07)
+			| (LIMIT(r+yTL) & 0xF8);
+
+		rgb[2] = ((LIMIT(b+yTR) >> 3) & 0x1F) 
+			| ((LIMIT(g+yTR) << 3) & 0xE0);
+		rgb[3] = ((LIMIT(g+yTR) >> 5) & 0x07) 
+			| (LIMIT(r+yTR) & 0xF8);
 
 		/* Skip down to next line to write out bottom two pixels */
 		rgb += 2 * rowPixels;
 
-		rgb[0] = ((LIMIT(b+yBL) >> 3) & 0x1F) | ((LIMIT(g+yBL) << 3) & 0xE0);
-		rgb[1] = ((LIMIT(g+yBL) >> 5) & 0x07) | (LIMIT(r+yBL) & 0xF8);
+		rgb[0] = ((LIMIT(b+yBL) >> 3) & 0x1F)
+			| ((LIMIT(g+yBL) << 3) & 0xE0);
+		rgb[1] = ((LIMIT(g+yBL) >> 5) & 0x07)
+			| (LIMIT(r+yBL) & 0xF8);
+
+		rgb[2] = ((LIMIT(b+yBR) >> 3) & 0x1F)
+			| ((LIMIT(g+yBR) << 3) & 0xE0);
+		rgb[3] = ((LIMIT(g+yBR) >> 5) & 0x07)
+			| (LIMIT(r+yBR) & 0xF8);
+	}
+}
+
+/**********************************************************************
+ *
+ * Raw data parsing
+ *
+ **********************************************************************/
+
+/* Copies a 64-byte segment at pIn to an 8x8 block at pOut. The width of the
+ * array at pOut is specified by w.
+ */
+static inline void 
+ov511_make_8x8(unsigned char *pIn, unsigned char *pOut, int w)
+{
+	unsigned char *pOut1 = pOut;
+	int x, y;
+
+	for (y = 0; y < 8; y++) {
+		pOut1 = pOut;
+		for (x = 0; x < 8; x++) {
+			*pOut1++ = *pIn++;
+		}
+		pOut += w;
+	}
+		
+}
+
+/*
+ * For RAW BW (YUV400) images, data shows up in 256 byte segments.
+ * The segments represent 4 squares of 8x8 pixels as follows:
+ *
+ *      0  1 ...  7    64  65 ...  71   ...  192 193 ... 199
+ *      8  9 ... 15    72  73 ...  79        200 201 ... 207
+ *           ...              ...                    ...
+ *     56 57 ... 63   120 121 ... 127        248 249 ... 255
+ *
+ */ 
+static void
+yuv400raw_to_yuv400p(struct ov511_frame *frame,
+		     unsigned char *pIn0, unsigned char *pOut0)
+{
+	int x, y;
+	unsigned char *pIn, *pOut, *pOutLine;
 
-		rgb[2] = ((LIMIT(b+yBR) >> 3) & 0x1F) | ((LIMIT(g+yBR) << 3) & 0xE0);
-		rgb[3] = ((LIMIT(g+yBR) >> 5) & 0x07) | (LIMIT(r+yBR) & 0xF8);
+	/* Copy Y */
+	pIn = pIn0;
+	pOutLine = pOut0;
+	for (y = 0; y < frame->rawheight - 1; y += 8) {
+		pOut = pOutLine;
+		for (x = 0; x < frame->rawwidth - 1; x += 8) {
+			ov511_make_8x8(pIn, pOut, frame->rawwidth);
+			pIn += 64;
+			pOut += 8;
+		}
+		pOutLine += 8 * frame->rawwidth;
 	}
 }
 
 /*
- * For a 640x480 YUV4:2:0 images, data shows up in 1200 384 byte segments.
+ * For YUV4:2:0 images, the data shows up in 384 byte segments.
  * The first 64 bytes of each segment are U, the next 64 are V.  The U and
  * V are arranged as follows:
  *
@@ -1239,7 +3377,9 @@
  *     56 57 ... 63   120 121 ... 127   ...  248 249 ... 255
  *
  * Note that the U and V data in one segment represents a 16 x 16 pixel
- * area, but the Y data represents a 32 x 8 pixel area.
+ * area, but the Y data represents a 32 x 8 pixel area. If the width is not an
+ * even multiple of 32, the extra 8x8 blocks within a 32x8 block belong to the
+ * next horizontal stripe.
  *
  * If dumppix module param is set, _parse_data just dumps the incoming segments,
  * verbatim, in order, into the frame. When used with vidcat -f ppm -s 640x480
@@ -1248,391 +3388,504 @@
  * this data is scrambled.
  */
 
-#define HDIV 8
-#define WDIV (256/HDIV)
-
+/* Converts from raw, uncompressed segments at pIn0 to a YUV420P frame at pOut0.
+ *
+ * FIXME: Currently only handles width and height that are multiples of 16
+ */
 static void
-ov511_parse_gbr422_to_rgb24(unsigned char *pIn0, unsigned char *pOut0,
-		       int iOutY, int iOutUV, int iHalf, int iWidth)
+yuv420raw_to_yuv420p(struct ov511_frame *frame,
+		     unsigned char *pIn0, unsigned char *pOut0)
 {
-	int k, l, m;
-	unsigned char *pIn;
-	unsigned char *pOut, *pOut1;
+	int k, x, y;
+	unsigned char *pIn, *pOut, *pOutLine;
+	const unsigned int a = frame->rawwidth * frame->rawheight;
+	const unsigned int w = frame->rawwidth / 2;
 
+	/* Copy U and V */
 	pIn = pIn0;
-	pOut = pOut0 + iOutUV + (force_rgb ? 2 : 0);
-		
-	for (k = 0; k < 8; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) {
-			*pOut1 = *(pOut1 + 3) = *(pOut1 + iWidth*3) =
-				*(pOut1 + iWidth*3 + 3) = *pIn++;
-			pOut1 += 6;
+	pOutLine = pOut0 + a;
+	for (y = 0; y < frame->rawheight - 1; y += 16) {
+		pOut = pOutLine;
+		for (x = 0; x < frame->rawwidth - 1; x += 16) {
+			ov511_make_8x8(pIn, pOut, w);
+			ov511_make_8x8(pIn + 64, pOut + a/4, w);
+			pIn += 384;
+			pOut += 8;
 		}
-		pOut += iWidth*3*2;
+		pOutLine += 8 * w;
 	}
 
-	pIn = pIn0 + 64;
-	pOut = pOut0 + iOutUV + (force_rgb ? 0 : 2);
-	for (k = 0; k < 8; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) {
-			*pOut1 = *(pOut1 + 3) = *(pOut1 + iWidth*3) =
-				*(pOut1 + iWidth*3 + 3) = *pIn++;
-			pOut1 += 6;
+	/* Copy Y */
+	pIn = pIn0 + 128;
+	pOutLine = pOut0;
+	k = 0;
+	for (y = 0; y < frame->rawheight - 1; y += 8) {
+		pOut = pOutLine;
+		for (x = 0; x < frame->rawwidth - 1; x += 8) {
+			ov511_make_8x8(pIn, pOut, frame->rawwidth);
+			pIn += 64;
+			pOut += 8;
+			if ((++k) > 3) {
+				k = 0;
+				pIn += 128;
+			}
+		}
+		pOutLine += 8 * frame->rawwidth;
+	}
+}
+
+/*
+ * fixFrameRGBoffset--
+ * My camera seems to return the red channel about 1 pixel
+ * low, and the blue channel about 1 pixel high. After YUV->RGB
+ * conversion, we can correct this easily. OSL 2/24/2000.
+ */
+static void 
+fixFrameRGBoffset(struct ov511_frame *frame)
+{
+	int x, y;
+	int rowBytes = frame->width*3, w = frame->width;
+	unsigned char *rgb = frame->data;
+	const int shift = 1;  /* Distance to shift pixels by, vertically */
+
+	/* Don't bother with little images */
+	if (frame->width < 400) 
+		return;
+
+	/* This only works with RGB24 */
+	if (frame->format != VIDEO_PALETTE_RGB24)
+		return;
+
+	/* Shift red channel up */
+	for (y = shift; y < frame->height; y++)	{
+		int lp = (y-shift)*rowBytes;     /* Previous line offset */
+		int lc = y*rowBytes;             /* Current line offset */
+		for (x = 0; x < w; x++)
+			rgb[lp+x*3+2] = rgb[lc+x*3+2]; /* Shift red up */
+	}
+
+	/* Shift blue channel down */
+	for (y = frame->height-shift-1; y >= 0; y--) {
+		int ln = (y + shift) * rowBytes;  /* Next line offset */
+		int lc = y * rowBytes;            /* Current line offset */
+		for (x = 0; x < w; x++)
+			rgb[ln+x*3+0] = rgb[lc+x*3+0]; /* Shift blue down */
+	}
+}
+
+/**********************************************************************
+ *
+ * Decompression
+ *
+ **********************************************************************/
+
+/* Chooses a decompression module, locks it, and sets ov511->decomp_ops
+ * accordingly. Returns -ENXIO if decompressor is not available, otherwise
+ * returns 0 if no other error.
+ */
+static int 
+ov51x_request_decompressor(struct usb_ov511 *ov511)
+{
+	if (!ov511)
+		return -ENODEV;
+
+	if (ov511->decomp_ops) {
+		err("ERROR: Decompressor already requested!");
+		return -EINVAL;
+	}
+
+	lock_kernel();
+
+	/* Try to get MMX, and fall back on no-MMX if necessary */
+	if (ov511->bridge == BRG_OV511 || ov511->bridge == BRG_OV511PLUS) {
+		if (ov511_mmx_decomp_ops) {
+			PDEBUG(3, "Using OV511 MMX decompressor");
+			ov511->decomp_ops = ov511_mmx_decomp_ops;
+		} else if (ov511_decomp_ops) {
+			PDEBUG(3, "Using OV511 decompressor");
+			ov511->decomp_ops = ov511_decomp_ops;
+		} else {
+			err("No decompressor available");
+		}
+	} else if (ov511->bridge == BRG_OV518 ||
+		   ov511->bridge == BRG_OV518PLUS) {
+		if (ov518_mmx_decomp_ops) {
+			PDEBUG(3, "Using OV518 MMX decompressor");
+			ov511->decomp_ops = ov518_mmx_decomp_ops;
+		} else if (ov518_decomp_ops) {
+			PDEBUG(3, "Using OV518 decompressor");
+			ov511->decomp_ops = ov518_decomp_ops;
+		} else {
+			err("No decompressor available");
 		}
-		pOut += iWidth*3*2;
+	} else {
+		err("Unknown bridge");
 	}
 
-	pIn = pIn0 + 128;
-	pOut = pOut0 + iOutY + 1;
-	for (k = 0; k < 4; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) {
-			for (m = 0; m < 8; m++) {
-				*pOut1 = *pIn++;
-				pOut1 += 3;
-			}
-			pOut1 += (iWidth - 8) * 3;
+	if (ov511->decomp_ops) {
+		if (!ov511->decomp_ops->decomp_lock) {
+			ov511->decomp_ops = NULL;
+			unlock_kernel();
+			return -ENOSYS;
 		}
-		pOut += 8 * 3;
+		ov511->decomp_ops->decomp_lock();
+		unlock_kernel();
+		return 0;
+	} else {
+		unlock_kernel();
+		return -ENXIO;
 	}
 }
 
-static void
-ov511_parse_yuv420_to_rgb(unsigned char *pIn0, unsigned char *pOut0,
-		       int iOutY, int iOutUV, int iHalf, int iWidth, int bits)
+/* Unlocks decompression module and nulls ov511->decomp_ops. Safe to call even
+ * if ov511->decomp_ops is NULL.
+ */
+static void 
+ov51x_release_decompressor(struct usb_ov511 *ov511)
 {
-	int k, l, m;
-	int bytes = bits >> 3;
-	unsigned char *pIn;
-	unsigned char *pOut, *pOut1;
-
-	/* Just copy the Y's if in the first stripe */
-	if (!iHalf) {
-		pIn = pIn0 + 128;
-		pOut = pOut0 + iOutY;
-		for (k = 0; k < 4; k++) {
-			pOut1 = pOut;
-			for (l = 0; l < 8; l++) {
-				for (m = 0; m < 8; m++) {
-					*pOut1 = *pIn++;
-					pOut1 += bytes;
-				}
-				pOut1 += (iWidth - 8) * bytes;
-			}
-			pOut += 8 * bytes;
-		}
-	}
+	int released = 0;	/* Did we actually do anything? */
 
-	/* Use the first half of VUs to calculate value */
-	pIn = pIn0;
-	pOut = pOut0 + iOutUV;
-	for (l = 0; l < 4; l++) {
-		for (m=0; m<8; m++) {
-			int y00 = *(pOut);
-			int y01 = *(pOut+bytes);
-			int y10 = *(pOut+iWidth*bytes);
-			int y11 = *(pOut+iWidth*bytes+bytes);
-			int v   = *(pIn+64) - 128;
-			int u   = *pIn++ - 128;
-			ov511_move_420_block(y00, y01, y10, y11, u, v, iWidth,
-				pOut, bits);
-			pOut += 2 * bytes;
-		}
-		pOut += (iWidth*2 - 16) * bytes;
+	if (!ov511)
+		return;
+
+	lock_kernel();
+
+	if (ov511->decomp_ops && ov511->decomp_ops->decomp_unlock) {
+		ov511->decomp_ops->decomp_unlock();
+		released = 1;
 	}
 
-	/* Just copy the other UV rows */
-	for (l = 0; l < 4; l++) {
-		for (m = 0; m < 8; m++) {
-			*pOut++ = *(pIn + 64);
-			*pOut = *pIn++;
-			pOut += 2 * bytes - 1;
-		}
-		pOut += (iWidth*2 - 16) * bytes;
-	}
-
-	/* Calculate values if it's the second half */
-	if (iHalf) {
-		pIn = pIn0 + 128;
-		pOut = pOut0 + iOutY;
-		for (k = 0; k < 4; k++) {
-			pOut1 = pOut;
-			for (l=0; l<4; l++) {
-				for (m=0; m<4; m++) {
-					int y10 = *(pIn+8);
-					int y00 = *pIn++;
-					int y11 = *(pIn+8);
-					int y01 = *pIn++;
-					int v   = *pOut1 - 128;
-					int u   = *(pOut1+1) - 128;
-					ov511_move_420_block(y00, y01, y10,
-						y11, u, v, iWidth, pOut1, bits);
-					pOut1 += 2 * bytes;
-				}
-				pOut1 += (iWidth*2 - 8) * bytes;
-				pIn += 8;
-			}
-			pOut += 8 * bytes;
-		}
+	ov511->decomp_ops = NULL;
+	
+	unlock_kernel();
+
+	if (released)
+		PDEBUG(3, "Decompressor released");
+}
+
+static void 
+ov51x_decompress(struct usb_ov511 *ov511, struct ov511_frame *frame,
+		 unsigned char *pIn0, unsigned char *pOut0)
+{
+	if (!ov511->decomp_ops)
+		if (ov51x_request_decompressor(ov511))
+			return;
+
+	PDEBUG(4, "Decompressing %d bytes", frame->bytes_recvd);
+
+	if (frame->format == VIDEO_PALETTE_GREY 
+	    && ov511->decomp_ops->decomp_400) {
+		int ret = ov511->decomp_ops->decomp_400(
+			pIn0,
+			pOut0,
+			frame->rawwidth,
+			frame->rawheight,
+			frame->bytes_recvd);
+		PDEBUG(4, "DEBUG: decomp_400 returned %d", ret);
+	} else if (ov511->decomp_ops->decomp_420) {
+		int ret = ov511->decomp_ops->decomp_420(
+			pIn0,
+			pOut0,
+			frame->rawwidth,
+			frame->rawheight,
+			frame->bytes_recvd);
+		PDEBUG(4, "DEBUG: decomp_420 returned %d", ret);
+	} else {
+		err("Decompressor does not support this format");
 	}
 }
 
-static void
-ov511_dumppix(unsigned char *pIn0, unsigned char *pOut0,
-	      int iOutY, int iOutUV, int iHalf, int iWidth)
-{
-	int i, j, k;
-	unsigned char *pIn, *pOut, *pOut1;
+/**********************************************************************
+ *
+ * Format conversion
+ *
+ **********************************************************************/
 
-	switch (dumppix) {
-	case 1: /* Just dump YUV data straight out for debug */
-		pOut0 += iOutY;
-		for (i = 0; i < HDIV; i++) {
-			for (j = 0; j < WDIV; j++) {
-				*pOut0++ = *pIn0++;
-				*pOut0++ = *pIn0++;
-				*pOut0++ = *pIn0++;
-			}
-			pOut0 += (iWidth - WDIV) * 3;
-		}
-		break;
-	case 2: /* This converts the Y data to "black-and-white" RGB data */
-		/* Useful for experimenting with compression */
-		pIn = pIn0 + 128;
-		pOut = pOut0 + iOutY;
-		for (i = 0; i < 4; i++) {
-			pOut1 = pOut;
-			for (j = 0; j < 8; j++) {
-				for (k = 0; k < 8; k++) {
-					*pOut1++ = *pIn;
-					*pOut1++ = *pIn;
-					*pOut1++ = *pIn++;
-				}
-				pOut1 += (iWidth - 8) * 3;
-			}
-			pOut += 8 * 3;
-		}
-		break;
-	case 3: /* This will dump only the Y channel data stream as-is */
-		pIn = pIn0 + 128;
-		pOut = pOut0 + output_offset;
-		for (i = 0; i < 256; i++) {
-			*pOut++ = *pIn;
-			*pOut++ = *pIn;
-			*pOut++ = *pIn++;
-			output_offset += 3;
-		}
-		break;
-	} /* End switch (dumppix) */
-}
+/* Converts from planar YUV420 to RGB24. */
+static void 
+yuv420p_to_rgb(struct ov511_frame *frame,
+	       unsigned char *pIn0, unsigned char *pOut0, int bits)
+{
+	const int numpix = frame->width * frame->height;
+	const int bytes = bits >> 3;
+	int i, j, y00, y01, y10, y11, u, v;
+	unsigned char *pY = pIn0;
+	unsigned char *pU = pY + numpix;
+	unsigned char *pV = pU + numpix / 4;
+	unsigned char *pOut = pOut0;
+
+	for (j = 0; j <= frame->height - 2; j += 2) {
+		for (i = 0; i <= frame->width - 2; i += 2) {
+			y00 = *pY;
+			y01 = *(pY + 1);
+			y10 = *(pY + frame->width);
+			y11 = *(pY + frame->width + 1);
+			u = (*pU++) - 128;
+			v = (*pV++) - 128;
 
-/* This converts YUV420 segments to YUYV */
-static void
-ov511_parse_data_yuv422(unsigned char *pIn0, unsigned char *pOut0,
-		        int iOutY, int iOutUV, int iWidth)
-{
-	int k, l, m;
-	unsigned char *pIn, *pOut, *pOut1;
+			ov511_move_420_block(y00, y01, y10, y11, u, v,
+					     frame->width, pOut, bits);
+	
+			pY += 2;
+			pOut += 2 * bytes;
 
-	pIn = pIn0 + 128;
-	pOut = pOut0 + iOutY;
-	for (k = 0; k < 4; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) {
-			for (m = 0; m < 8; m++) {
-				*pOut1 = (*pIn++);
-				pOut1 += 2;
-			}
-			pOut1 += (iWidth - 8) * 2;
 		}
-		pOut += 8 * 2;
+		pY += frame->width;
+		pOut += frame->width * bytes;
 	}
+}
 
-	pIn = pIn0;
-	pOut = pOut0 + iOutUV + 1;
-	for (l = 0; l < 8; l++) {
-		for (m=0; m<8; m++) {
-			int v   = *(pIn+64);
-			int u   = *pIn++;
+/* Converts from planar YUV420 to YUV422 (YUYV). */
+static void
+yuv420p_to_yuv422(struct ov511_frame *frame,
+		  unsigned char *pIn0, unsigned char *pOut0)
+{
+	const int numpix = frame->width * frame->height;
+	int i, j;
+	unsigned char *pY = pIn0;
+	unsigned char *pU = pY + numpix;
+	unsigned char *pV = pU + numpix / 4;
+	unsigned char *pOut = pOut0;
+
+	for (i = 0; i < numpix; i++) {
+		*pOut = *(pY + i);
+		pOut += 2;
+	}
+
+	pOut = pOut0 + 1;
+	for (j = 0; j <= frame->height - 2 ; j += 2) {
+		for (i = 0; i <= frame->width - 2; i += 2) {
+			int u = *pU++;
+			int v = *pV++;
 			
 			*pOut = u;
 			*(pOut+2) = v;
-			*(pOut+iWidth) = u;
-			*(pOut+iWidth+2) = v;
+			*(pOut+frame->width*2) = u;
+			*(pOut+frame->width*2+2) = v;
 			pOut += 4;
 		}
-		pOut += (iWidth*4 - 32);
+		pOut += (frame->width * 2);
 	}
 }
 
+/* Converts pData from planar YUV420 to planar YUV422 **in place**. */
 static void
-ov511_parse_data_yuv420(unsigned char *pIn0, unsigned char *pOut0,
-		        int iOutY, int iOutUV, int iWidth, int iHeight)
+yuv420p_to_yuv422p(struct ov511_frame *frame, unsigned char *pData)
 {
-	int k, l, m;
-	unsigned char *pIn;
-	unsigned char *pOut, *pOut1;
-	unsigned a = iWidth * iHeight;
-	unsigned w = iWidth / 2;
-
-	pIn = pIn0;
-	pOut = pOut0 + iOutUV + a;
-	for (k = 0; k < 8; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) *pOut1++ = *pIn++;
+	const int numpix = frame->width * frame->height;
+	const int w = frame->width;
+	int j;
+	unsigned char *pIn, *pOut;
+
+	/* Clear U and V */
+	memset(pData + numpix + numpix / 2, 127, numpix / 2);
+
+	/* Convert V starting from beginning and working forward */
+	pIn = pData + numpix + numpix / 4;
+	pOut = pData + numpix +numpix / 2;
+	for (j = 0; j <= frame->height - 2; j += 2) {
+		memmove(pOut, pIn, w/2);
+		memmove(pOut + w/2, pIn, w/2);
+		pIn += w/2;
 		pOut += w;
 	}
 
-	pIn = pIn0 + 64;
-	pOut = pOut0 + iOutUV + a + a/4;
-	for (k = 0; k < 8; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) *pOut1++ = *pIn++;
-		pOut += w;
-	}
-
-	pIn = pIn0 + 128;
-	pOut = pOut0 + iOutY;
-	for (k = 0; k < 4; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) {
-			for (m = 0; m < 8; m++)
-				*pOut1++ =*pIn++;
-			pOut1 += iWidth - 8;
-		}
-		pOut += 8;
+	/* Convert U, starting from end and working backward */
+	pIn = pData + numpix + numpix / 4;
+	pOut = pData + numpix + numpix / 2;
+	for (j = 0; j <= frame->height - 2; j += 2) {
+		pIn -= w/2;
+		pOut -= w;
+		memmove(pOut, pIn, w/2);
+		memmove(pOut + w/2, pIn, w/2);
 	}
 }
 
+/* Fuses even and odd fields together, and doubles width.
+ * INPUT: an odd field followed by an even field at pIn0, in YUV planar format
+ * OUTPUT: a normal YUV planar image, with correct aspect ratio
+ */
 static void
-ov511_parse_data_yuv422p(unsigned char *pIn0, unsigned char *pOut0,
-		       int iOutY, int iOutUV, int iWidth, int iHeight)
+deinterlace(struct ov511_frame *frame, int rawformat,
+            unsigned char *pIn0, unsigned char *pOut0)
 {
-	int k, l, m;
-	unsigned char *pIn;
-	unsigned char *pOut, *pOut1;
-	unsigned a = iWidth * iHeight;
-	unsigned w = iWidth / 2;
+	const int fieldheight = frame->rawheight / 2;
+	const int fieldpix = fieldheight * frame->rawwidth;
+	const int w = frame->width;
+	int x, y;
+	unsigned char *pInEven, *pInOdd, *pOut;
 
-	pIn = pIn0;
-	pOut = pOut0 + iOutUV + a;
-	for (k = 0; k < 8; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) {
-			*pOut1 = *(pOut1 + w) = *pIn++;
-			pOut1++;
-		}
-		pOut += iWidth;
+	PDEBUG(5, "fieldheight=%d", fieldheight);
+
+	if (frame->rawheight != frame->height) {
+		err("invalid height");
+		return;
 	}
 
-	pIn = pIn0 + 64;
-	pOut = pOut0 + iOutUV + a + a/2;
-	for (k = 0; k < 8; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) {
-			*pOut1 = *(pOut1 + w) = *pIn++;
-			pOut1++;
-		}
-		pOut += iWidth;
+	if ((frame->rawwidth * 2) != frame->width) {
+		err("invalid width");
+		return;
 	}
 
-	pIn = pIn0 + 128;
-	pOut = pOut0 + iOutY;
-	for (k = 0; k < 4; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) {
-			for (m = 0; m < 8; m++)
-				*pOut1++ =*pIn++;
-			pOut1 += iWidth - 8;
+	/* Y */
+	pInOdd = pIn0;
+	pInEven = pInOdd + fieldpix;
+	pOut = pOut0;
+	for (y = 0; y < fieldheight; y++) {
+		for (x = 0; x < frame->rawwidth; x++) {
+			*pOut = *pInEven;
+			*(pOut+1) = *pInEven++;
+			*(pOut+w) = *pInOdd;
+			*(pOut+w+1) = *pInOdd++;
+			pOut += 2;
 		}
-		pOut += 8;
+		pOut += w;
 	}
-}
-
-/*
- * For 640x480 RAW BW images, data shows up in 1200 256 byte segments.
- * The segments represent 4 squares of 8x8 pixels as follows:
- *
- *      0  1 ...  7    64  65 ...  71   ...  192 193 ... 199
- *      8  9 ... 15    72  73 ...  79        200 201 ... 207
- *           ...              ...                    ...
- *     56 57 ... 63   120 121 ... 127        248 249 ... 255
- *
- */ 
-static void
-ov511_parse_data_grey(unsigned char *pIn0, unsigned char *pOut0,
-		      int iOutY, int iWidth)		    
-{
-	int k, l, m;
-	unsigned char *pIn;
-	unsigned char *pOut, *pOut1;
 
-	pIn = pIn0;
-	pOut = pOut0 + iOutY;
-	for (k = 0; k < 4; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) {
-			for (m = 0; m < 8; m++) {
-				*pOut1++ = *pIn++;
+	if (rawformat == RAWFMT_YUV420) {
+	/* U */
+		pInOdd = pIn0 + fieldpix * 2;
+		pInEven = pInOdd + fieldpix / 4;
+		for (y = 0; y < fieldheight / 2; y++) {
+			for (x = 0; x < frame->rawwidth / 2; x++) {
+				*pOut = *pInEven;
+				*(pOut+1) = *pInEven++;
+				*(pOut+w/2) = *pInOdd;
+				*(pOut+w/2+1) = *pInOdd++;
+				pOut += 2;
+			}
+			pOut += w/2;
+		}
+	/* V */
+		pInOdd = pIn0 + fieldpix * 2 + fieldpix / 2;
+		pInEven = pInOdd + fieldpix / 4;
+		for (y = 0; y < fieldheight / 2; y++) {
+			for (x = 0; x < frame->rawwidth / 2; x++) {
+				*pOut = *pInEven;
+				*(pOut+1) = *pInEven++;
+				*(pOut+w/2) = *pInOdd;
+				*(pOut+w/2+1) = *pInOdd++;
+				pOut += 2;
 			}
-			pOut1 += iWidth - 8;
+			pOut += w/2;
 		}
-		pOut += 8;
 	}
 }
 
-/*
- * fixFrameRGBoffset--
- * My camera seems to return the red channel about 1 pixel
- * low, and the blue channel about 1 pixel high. After YUV->RGB
- * conversion, we can correct this easily. OSL 2/24/2000.
+/* Post-processes the specified frame. This consists of:
+ * 	1. Decompress frame, if necessary
+ *	2. Deinterlace frame and scale to proper size, if necessary
+ * 	3. Convert from YUV planar to destination format, if necessary
+ * 	4. Fix the RGB offset, if necessary
  */
-static void fixFrameRGBoffset(struct ov511_frame *frame)
+static void 
+ov511_postprocess(struct usb_ov511 *ov511, struct ov511_frame *frame)
 {
-	int x, y;
-	int rowBytes = frame->width*3, w = frame->width;
-	unsigned char *rgb = frame->data;
-	const int shift = 1;  /* Distance to shift pixels by, vertically */
+	if (dumppix) {
+		memset(frame->data, 0, 
+			MAX_DATA_SIZE(ov511->maxwidth, ov511->maxheight));
+		PDEBUG(4, "Dumping %d bytes", frame->bytes_recvd);
+		memmove(frame->data, frame->rawdata, frame->bytes_recvd);
+		return;
+	}
+
+	/* YUV400 must be handled separately */
+	if (frame->format == VIDEO_PALETTE_GREY) {
+		/* Deinterlace frame, if necessary */
+		if (ov511->sensor == SEN_SAA7111A && frame->rawheight == 480) {
+			if (frame->compressed)
+				ov51x_decompress(ov511, frame, frame->rawdata,
+						 frame->tempdata);
+			else
+				yuv400raw_to_yuv400p(frame, frame->rawdata,
+						     frame->tempdata);
+
+			deinterlace(frame, RAWFMT_YUV400, frame->tempdata,
+			            frame->data);
+		} else {
+			if (frame->compressed)
+				ov51x_decompress(ov511, frame, frame->rawdata,
+						 frame->data);
+			else
+				yuv400raw_to_yuv400p(frame, frame->rawdata,
+						     frame->data);
+		}
 
-	/* Don't bother with little images */
-	if (frame->width < 400) 
 		return;
+	}
 
-	/* Shift red channel up */
-	for (y = shift; y < frame->height; y++)	{
-		int lp = (y-shift)*rowBytes;     /* Previous line offset */
-		int lc = y*rowBytes;             /* Current line offset */
-		for (x = 0; x < w; x++)
-			rgb[lp+x*3+2] = rgb[lc+x*3+2]; /* Shift red up */
+	/* Process frame->data to frame->rawdata */
+	if (frame->compressed)
+		ov51x_decompress(ov511, frame, frame->rawdata, frame->tempdata);
+	else
+		yuv420raw_to_yuv420p(frame, frame->rawdata, frame->tempdata);
+
+	/* Deinterlace frame, if necessary */
+	if (ov511->sensor == SEN_SAA7111A && frame->rawheight == 480) {
+		memmove(frame->rawdata, frame->tempdata,
+			MAX_RAW_DATA_SIZE(frame->width, frame->height));
+		deinterlace(frame, RAWFMT_YUV420, frame->rawdata,
+		            frame->tempdata);
 	}
 
-	/* Shift blue channel down */
-	for (y = frame->height-shift-1; y >= 0; y--) {
-		int ln = (y + shift) * rowBytes;  /* Next line offset */
-		int lc = y * rowBytes;            /* Current line offset */
-		for (x = 0; x < w; x++)
-			rgb[ln+x*3+0] = rgb[lc+x*3+0]; /* Shift blue down */
+	/* Frame should be (width x height) and not (rawwidth x rawheight) at
+         * this point. */
+
+#if 0
+	/* Clear output buffer for testing purposes */
+	memset(frame->data, 0, MAX_DATA_SIZE(frame->width, frame->height));
+#endif
+
+	/* Process frame->tempdata to frame->data */
+	switch (frame->format) {
+	case VIDEO_PALETTE_RGB565:
+		yuv420p_to_rgb(frame, frame->tempdata, frame->data, 16);
+		break;
+	case VIDEO_PALETTE_RGB24:
+		yuv420p_to_rgb(frame, frame->tempdata, frame->data, 24);
+		break;
+	case VIDEO_PALETTE_YUV422:
+	case VIDEO_PALETTE_YUYV:
+		yuv420p_to_yuv422(frame, frame->tempdata, frame->data);
+		break;
+	case VIDEO_PALETTE_YUV420:
+	case VIDEO_PALETTE_YUV420P:
+		memmove(frame->data, frame->tempdata,
+			MAX_RAW_DATA_SIZE(frame->width, frame->height));
+		break;
+	case VIDEO_PALETTE_YUV422P:
+		/* Data is converted in place, so copy it in advance */
+		memmove(frame->data, frame->tempdata,
+			MAX_RAW_DATA_SIZE(frame->width, frame->height));
+
+		yuv420p_to_yuv422p(frame, frame->data);
+		break;
+	default:
+		err("Cannot convert data to this format");
 	}
+
+	if (fix_rgb_offset)
+		fixFrameRGBoffset(frame);
 }
 
 /**********************************************************************
  *
- * OV511 data transfer, IRQ handler
+ * OV51x data transfer, IRQ handler
  *
  **********************************************************************/
 
-static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
+static int 
+ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
 {
 	unsigned char *cdata;
-	int i, totlen = 0;
-	int aPackNum[10];
+	int data_size, num, offset, i, totlen = 0;
+	int aPackNum[FRAMES_PER_DESC];
 	struct ov511_frame *frame;
-	unsigned char *pData;
-	int iPix;
+	struct timeval *ts;
+
+	PDEBUG(5, "Moving %d packets", urb->number_of_packets);
 
-	PDEBUG (4, "Moving %d packets", urb->number_of_packets);
+	data_size = ov511->packet_size - 1;
 
 	for (i = 0; i < urb->number_of_packets; i++) {
 		int n = urb->iso_frame_desc[i].actual_length;
@@ -1652,44 +3905,65 @@
 			PDEBUG(2, "data error: [%d] len=%d, status=%d", i, n, st);
 
 		frame = &ov511->frame[ov511->curframe];
-		
+
 		/* SOF/EOF packets have 1st to 8th bytes zeroed and the 9th
 		 * byte non-zero. The EOF packet has image width/height in the
-		 * 10th and 11th packets. The 9th bit is given as follows:
+		 * 10th and 11th bytes. The 9th byte is given as follows:
 		 *
 		 * bit 7: EOF
 		 *     6: compression enabled
 		 *     5: 422/420/400 modes
 		 *     4: 422/420/400 modes
 		 *     3: 1
-		 *     2: snapshot bottom on
+		 *     2: snapshot button on
 		 *     1: snapshot frame
 		 *     0: even/odd field
 		 */
 
+		if (printph) {
+			info("packet header (%3d): %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x",
+				cdata[ov511->packet_size - 1],
+				cdata[0], cdata[1], cdata[2], cdata[3], cdata[4], cdata[5],
+				cdata[6], cdata[7], cdata[8], cdata[9], cdata[10], cdata[11]);
+		}
+
 		/* Check for SOF/EOF packet */
-		if ((cdata[0] | cdata[1] | cdata[2] | cdata[3] | 
+		if ((cdata[0] | cdata[1] | cdata[2] | cdata[3] |
 		     cdata[4] | cdata[5] | cdata[6] | cdata[7]) ||
 		     (~cdata[8] & 0x08))
 			goto check_middle;
 
 		/* Frame end */
 		if (cdata[8] & 0x80) {
-			struct timeval *ts;
-
-			ts = (struct timeval *)(frame->data + MAX_FRAME_SIZE);
-			do_gettimeofday (ts);
-
-		 	PDEBUG(4, "Frame end, curframe = %d, packnum=%d, hw=%d, vw=%d",
-				ov511->curframe, (int)(cdata[ov511->packet_size - 1]),
-				(int)(cdata[9]), (int)(cdata[10]));
+			ts = (struct timeval *)(frame->data 
+			      + MAX_FRAME_SIZE(ov511->maxwidth, ov511->maxheight));
+			do_gettimeofday(ts);
+
+			/* Get the actual frame size from the EOF header */
+			frame->rawwidth = ((int)(cdata[9]) + 1) * 8;
+			frame->rawheight = ((int)(cdata[10]) + 1) * 8;
+
+	 		PDEBUG(4, "Frame end, curframe = %d, packnum=%d, hw=%d, vw=%d, recvd=%d",
+				ov511->curframe,
+				(int)(cdata[ov511->packet_size - 1]),
+				frame->rawwidth,
+				frame->rawheight,
+				frame->bytes_recvd);
+
+			/* Validate the header data */
+			RESTRICT_TO_RANGE(frame->rawwidth, ov511->minwidth, ov511->maxwidth);
+			RESTRICT_TO_RANGE(frame->rawheight, ov511->minheight, ov511->maxheight);
+
+			/* Don't allow byte count to exceed buffer size */
+			RESTRICT_TO_RANGE(frame->bytes_recvd,
+					  8, 
+					  MAX_RAW_DATA_SIZE(ov511->maxwidth,
+					                    ov511->maxheight));
 
 			if (frame->scanstate == STATE_LINES) {
 		    		int iFrameNext;
 
-				if (fix_rgb_offset)
-					fixFrameRGBoffset(frame);
-				frame->grabstate = FRAME_DONE;
+				frame->grabstate = FRAME_DONE;	// FIXME: Is this right?
 
 				if (waitqueue_active(&frame->wq)) {
 					frame->grabstate = FRAME_DONE;
@@ -1713,6 +3987,8 @@
 
 					ov511->curframe = -1;
 				}
+			} else {
+				PDEBUG(5, "Frame done, but not scanning");
 			}
 			/* Image corruption caused by misplaced frame->segment = 0
 			 * fixed by carlosf@conectiva.com.br
@@ -1721,13 +3997,6 @@
 			/* Frame start */
 			PDEBUG(4, "Frame start, framenum = %d", ov511->curframe);
 
-#if 0
-			/* Make sure no previous data carries over; necessary
-			 * for compression experimentation */
-			memset(frame->data, 0, MAX_DATA_SIZE);
-#endif
-			output_offset = 0;
-
 			/* Check to see if it's a snapshot frame */
 			/* FIXME?? Should the snapshot reset go here? Performance? */
 			if (cdata[8] & 0x02) {
@@ -1736,129 +4005,324 @@
 			}
 
 			frame->scanstate = STATE_LINES;
-			frame->segment = 0;
+			frame->bytes_recvd = 0;
+			frame->compressed = cdata[8] & 0x40;
 		}
 
 check_middle:
 		/* Are we in a frame? */
-		if (frame->scanstate != STATE_LINES)
+		if (frame->scanstate != STATE_LINES) {
+			PDEBUG(5, "Not in a frame; packet skipped");
+			continue;
+		}
+
+#if 0
+		/* Skip packet if first 9 bytes are zero. These are common, so
+		 * we use a less expensive test here instead of later */
+		if (frame->compressed) {
+			int b, skip = 1;
+
+			for (b = 0; b < 9; b++) { 
+				if (cdata[b])
+					skip=0;
+			}
+
+			if (skip) {
+				PDEBUG(5, "Skipping packet (all zero)");
+				continue;
+			}
+		}
+#endif
+		/* If frame start, skip header */
+		if (frame->bytes_recvd == 0)
+			offset = 9;
+		else
+			offset = 0;
+
+		num = n - offset - 1;
+
+		/* Dump all data exactly as received */
+		if (dumppix == 2) {
+			frame->bytes_recvd += n - 1;
+			if (frame->bytes_recvd <= MAX_RAW_DATA_SIZE(ov511->maxwidth, ov511->maxheight))
+				memmove(frame->rawdata + frame->bytes_recvd - (n - 1),
+					&cdata[0], n - 1);
+			else
+				PDEBUG(3, "Raw data buffer overrun!! (%d)",
+					frame->bytes_recvd
+					- MAX_RAW_DATA_SIZE(ov511->maxwidth,
+							    ov511->maxheight));
+		} else if (!frame->compressed && !remove_zeros) {
+			frame->bytes_recvd += num;
+			if (frame->bytes_recvd <= MAX_RAW_DATA_SIZE(ov511->maxwidth, ov511->maxheight))
+				memmove(frame->rawdata + frame->bytes_recvd - num,
+					&cdata[offset], num);
+			else
+				PDEBUG(3, "Raw data buffer overrun!! (%d)",
+					frame->bytes_recvd
+					- MAX_RAW_DATA_SIZE(ov511->maxwidth,
+							    ov511->maxheight));
+		} else { /* Remove all-zero FIFO lines (aligned 32-byte blocks) */
+			int b, in = 0, allzero, copied=0;
+			if (offset) {
+				frame->bytes_recvd += 32 - offset;	// Bytes out
+				memmove(frame->rawdata,
+					&cdata[offset], 32 - offset);
+				in += 32;
+			}
+
+			while (in < n - 1) {
+				allzero = 1;
+				for (b = 0; b < 32; b++) {
+					if (cdata[in + b]) {
+						allzero = 0;
+						break;
+					}
+				}
+
+				if (allzero) {
+					/* Don't copy it */
+				} else {
+					if (frame->bytes_recvd + copied + 32
+					    <= MAX_RAW_DATA_SIZE(ov511->maxwidth, ov511->maxheight)) {
+						memmove(frame->rawdata + frame->bytes_recvd + copied,
+							&cdata[in], 32);
+						copied += 32;
+					} else {
+						PDEBUG(3, "Raw data buffer overrun!!");
+					}
+				}
+				in += 32;
+			}
+
+			frame->bytes_recvd += copied;
+		}
+
+	}
+
+	PDEBUG(5, "pn: %d %d %d %d %d %d %d %d %d %d",
+		aPackNum[0], aPackNum[1], aPackNum[2], aPackNum[3], aPackNum[4],
+		aPackNum[5],aPackNum[6], aPackNum[7], aPackNum[8], aPackNum[9]);
+
+	return totlen;
+}
+
+static int 
+ov518_move_data(struct usb_ov511 *ov511, urb_t *urb)
+{
+	unsigned char *cdata;
+	int i, data_size, totlen = 0;
+	struct ov511_frame *frame;
+	struct timeval *ts;
+
+	PDEBUG(5, "Moving %d packets", urb->number_of_packets);
+
+	/* OV518(+) has no packet numbering */
+	data_size = ov511->packet_size;
+
+	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;
+
+		urb->iso_frame_desc[i].actual_length = 0;
+		urb->iso_frame_desc[i].status = 0;
+
+		cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+
+		if (!n) {
+			PDEBUG(4, "Zero-length packet");
 			continue;
+		}
+
+		if (ov511->curframe == -1) {
+			PDEBUG(4, "No frame currently active");
+			continue;
+		}
+
+		if (st)
+			PDEBUG(2, "data error: [%d] len=%d, status=%d", i, n, st);
+
+		frame = &ov511->frame[ov511->curframe];
+
+#if 0
+		{
+			int d;
+			/* Print all data */
+			for (d = 0; d <= data_size - 16; d += 16) {
+				info("%4x: %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x", d,
+					cdata[d], cdata[d+1], cdata[d+2], cdata[d+3],
+					cdata[d+4], cdata[d+5], cdata[d+6], cdata[d+7],
+					cdata[d+8], cdata[d+9], cdata[d+10], cdata[d+11],
+					cdata[d+12], cdata[d+13], cdata[d+14], cdata[d+15]);
+			}
+		}
+#endif
+
+		if (printph) {
+			info("packet header: %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x",
+				cdata[0], cdata[1], cdata[2], cdata[3], cdata[4], cdata[5],
+				cdata[6], cdata[7], cdata[8], cdata[9], cdata[10], cdata[11]);
+		}
 
-		/* Deal with leftover from last segment, if any */
-		if (frame->segment) {
-			pData = ov511->scratch;
-			iPix = -ov511->scratchlen;
-			memmove(pData + ov511->scratchlen, cdata,
-				iPix+frame->segsize);
+		/* A false positive here is likely, until OVT gives me
+		 * the definitive SOF/EOF format */
+		if ((!(cdata[0] | cdata[1] | cdata[2] | cdata[3] |
+		      cdata[5])) && cdata[6]) {
+			
+			if (frame->scanstate == STATE_LINES) {
+				PDEBUG(4, "Detected frame end/start");
+				goto eof;
+			} else { //scanstate == STATE_SCANNING
+				/* Frame start */
+				PDEBUG(4, "Frame start, framenum = %d", ov511->curframe);
+				goto sof;
+			}
 		} else {
-			pData = &cdata[iPix = 9];
-	 	}
+			goto check_middle;
+		}
+	
+eof:
+		ts = (struct timeval *)(frame->data
+		      + MAX_FRAME_SIZE(ov511->maxwidth, ov511->maxheight));
+		do_gettimeofday(ts);
+
+ 		PDEBUG(4, "Frame end, curframe = %d, hw=%d, vw=%d, recvd=%d",
+			ov511->curframe,
+			(int)(cdata[9]), (int)(cdata[10]), frame->bytes_recvd);
+
+		// FIXME: Since we don't know the header formats yet,
+		// there is no way to know what the actual image size is
+		frame->rawwidth = frame->width;
+		frame->rawheight = frame->height;
+
+		/* Validate the header data */
+		RESTRICT_TO_RANGE(frame->rawwidth, ov511->minwidth, ov511->maxwidth);
+		RESTRICT_TO_RANGE(frame->rawheight, ov511->minheight, ov511->maxheight);
+
+		/* Don't allow byte count to exceed buffer size */
+		RESTRICT_TO_RANGE(frame->bytes_recvd,
+				  8, 
+				  MAX_RAW_DATA_SIZE(ov511->maxwidth, ov511->maxheight));
 
-		/* Parse the segments */
-		while (iPix <= (ov511->packet_size - 1) - frame->segsize &&
-		    frame->segment < frame->width * frame->height / 256) {
-			int iSegY, iSegUV;
-			int iY, jY, iUV, jUV;
-			int iOutY, iOutYP, iOutUV, iOutUVP;
-			unsigned char *pOut;
-
-			iSegY = iSegUV = frame->segment;
-			pOut = frame->data;
-			frame->segment++;
-			iPix += frame->segsize;
-
-			/* Handle subwindow */
-			if (frame->sub_flag) {
-				int iSeg1;
-
-				iSeg1 = iSegY / (ov511->subw / 32);
-				iSeg1 *= frame->width / 32;
-				iSegY = iSeg1 + (iSegY % (ov511->subw / 32));
-				if (iSegY >= frame->width * ov511->subh / 256)
-					break;
-
-				iSeg1 = iSegUV / (ov511->subw / 16);
-				iSeg1 *= frame->width / 16;
-				iSegUV = iSeg1 + (iSegUV % (ov511->subw / 16));
-
-				pOut += (ov511->subx + ov511->suby * frame->width) *
-					(frame->depth >> 3);
-			}
-
-			/* 
-			 * i counts segment lines
-			 * j counts segment columns
-			 * iOut is the offset (in bytes) of the upper left corner
-			 */
-			iY = iSegY / (frame->width / WDIV);
-			jY = iSegY - iY * (frame->width / WDIV);
-			iOutYP = iY*HDIV*frame->width + jY*WDIV;
-			iOutY = iOutYP * (frame->depth >> 3);
-			iUV = iSegUV / (frame->width / WDIV * 2);
-			jUV = iSegUV - iUV * (frame->width / WDIV * 2);
-			iOutUVP = iUV*HDIV*2*frame->width + jUV*WDIV/2;
-			iOutUV = iOutUVP * (frame->depth >> 3);
-
-			switch (frame->format) {
-			case VIDEO_PALETTE_GREY:
-				ov511_parse_data_grey (pData, pOut, iOutY, frame->width);
-				break;
-			case VIDEO_PALETTE_RGB24:
-				if (dumppix)
-					ov511_dumppix(pData, pOut, iOutY, iOutUV,
-						iY & 1, frame->width);
-				else if (sensor_gbr)
-					ov511_parse_gbr422_to_rgb24(pData, pOut, iOutY, iOutUV,
-						iY & 1, frame->width);
-				else
-					ov511_parse_yuv420_to_rgb(pData, pOut, iOutY, iOutUV,
-						iY & 1, frame->width, 24);
-				break;
-			case VIDEO_PALETTE_RGB565:
-				ov511_parse_yuv420_to_rgb(pData, pOut, iOutY, iOutUV,
-					iY & 1, frame->width, 16);
-				break;
-			case VIDEO_PALETTE_YUV422:
-			case VIDEO_PALETTE_YUYV:
-				ov511_parse_data_yuv422(pData, pOut, iOutY, iOutUV, frame->width);
-				break;
-			case VIDEO_PALETTE_YUV420:
-				ov511_parse_data_yuv420 (pData, pOut, iOutYP, iUV*HDIV*frame->width/2 + jUV*WDIV/4,
-					frame->width, frame->height);
-				break;
-			case VIDEO_PALETTE_YUV422P:
-				ov511_parse_data_yuv422p (pData, pOut, iOutYP, iOutUVP/2,
-					frame->width, frame->height);
-				break;
-			default:
-				err("Unsupported format: %d", frame->format);
+		if (frame->scanstate == STATE_LINES) {
+	    		int iFrameNext;
+
+			frame->grabstate = FRAME_DONE;	// FIXME: Is this right?
+
+			if (waitqueue_active(&frame->wq)) {
+				frame->grabstate = FRAME_DONE;
+				wake_up_interruptible(&frame->wq);
 			}
 
-			pData = &cdata[iPix];
-		}
+			/* If next frame is ready or grabbing,
+			 * point to it */
+			iFrameNext = (ov511->curframe + 1) % OV511_NUMFRAMES;
+			if (ov511->frame[iFrameNext].grabstate == FRAME_READY
+			    || ov511->frame[iFrameNext].grabstate == FRAME_GRABBING) {
+				ov511->curframe = iFrameNext;
+				ov511->frame[iFrameNext].scanstate = STATE_SCANNING;
+				frame = &ov511->frame[iFrameNext];
+			} else {
+				if (frame->grabstate == FRAME_DONE) {
+					PDEBUG(4, "Frame done! congratulations");
+				} else {
+					PDEBUG(4, "Frame not ready? state = %d",
+						ov511->frame[iFrameNext].grabstate);
+				}
+
+				ov511->curframe = -1;
+				PDEBUG(4, "SOF dropped (no active frame)");
+				continue;  /* Nowhere to store this frame */
+			}
+		}
+		/* Image corruption caused by misplaced frame->segment = 0
+		 * fixed by carlosf@conectiva.com.br
+		 */
+sof:
+		PDEBUG(4, "Starting capture on frame %d", frame->framenum);
+// Snapshot not reverse-engineered yet.
+#if 0
+		/* Check to see if it's a snapshot frame */
+		/* FIXME?? Should the snapshot reset go here? Performance? */
+		if (cdata[8] & 0x02) {
+			frame->snapshot = 1;
+			PDEBUG(3, "snapshot detected");
+		}
+#endif
+		frame->scanstate = STATE_LINES;
+		frame->bytes_recvd = 0;
+//		frame->compressed = 1;
+
+check_middle:
+		/* Are we in a frame? */
+		if (frame->scanstate != STATE_LINES) {
+			PDEBUG(4, "scanstate: no SOF yet");
+			continue;
+		}
+
+		/* Dump all data exactly as received */
+		if (dumppix == 2) {
+			frame->bytes_recvd += n;
+			if (frame->bytes_recvd <= MAX_RAW_DATA_SIZE(ov511->maxwidth, ov511->maxheight))
+				memmove(frame->rawdata + frame->bytes_recvd - n,
+					&cdata[0], n);
+			else
+				PDEBUG(3, "Raw data buffer overrun!! (%d)",
+					frame->bytes_recvd
+					- MAX_RAW_DATA_SIZE(ov511->maxwidth,
+							    ov511->maxheight));
+		} else {
+			/* All incoming data are divided into 8-byte segments. If the
+			 * segment contains all zero bytes, it must be skipped. These
+			 * zero-segments allow the OV518 to mainain a constant data rate
+			 * regardless of the effectiveness of the compression. Segments
+			 * are aligned relative to the beginning of each isochronous
+			 * packet. The first segment is a header.
+			 */
+
+			int b, in = 0, allzero, copied=0;
+
+// Decompressor expects the header
+#if 0
+			if (frame->bytes_recvd == 0)
+				in += 8;  /* Skip header */
+#endif
+
+			while (in < n) {
+				allzero = 1;
+				for (b = 0; b < 8; b++) {
+					if (cdata[in + b]) {
+						allzero = 0;
+						break;
+					}
+				}
 
-		/* Save extra data for next time */
-		if (frame->segment < frame->width * frame->height / 256) {
-			ov511->scratchlen = (ov511->packet_size - 1) - iPix;
-			if (ov511->scratchlen < frame->segsize)
-				memmove(ov511->scratch, pData, ov511->scratchlen);
-			else
-				ov511->scratchlen = 0;
+				if (allzero) {
+				/* Don't copy it */
+				} else {
+					if (frame->bytes_recvd + copied + 8
+					    <= MAX_RAW_DATA_SIZE(ov511->maxwidth, ov511->maxheight)) {
+						memmove(frame->rawdata + frame->bytes_recvd + copied,
+							&cdata[in], 8);
+						copied += 8;
+					} else {
+						PDEBUG(3, "Raw data buffer overrun!!");
+					}
+				}
+				in += 8;
+			}
+			frame->bytes_recvd += copied;
 		}
 	}
 
-	PDEBUG(5, "pn: %d %d %d %d %d %d %d %d %d %d",
-	       aPackNum[0], aPackNum[1], aPackNum[2], aPackNum[3], aPackNum[4],
-	       aPackNum[5],aPackNum[6], aPackNum[7], aPackNum[8], aPackNum[9]);
-
 	return totlen;
 }
 
-static void ov511_isoc_irq(struct urb *urb)
+static void 
+ov511_isoc_irq(struct urb *urb)
 {
 	int len;
 	struct usb_ov511 *ov511;
-	struct ov511_sbuf *sbuf;
 
 	if (!urb->context) {
 		PDEBUG(4, "no context");
@@ -1876,60 +4340,86 @@
 		PDEBUG(4, "hmmm... not streaming, but got interrupt");
 		return;
 	}
-	
-	sbuf = &ov511->sbuf[ov511->cursbuf];
 
-	/* Copy the data received into our scratch buffer */
+	/* Copy the data received into our frame buffer */
 	if (ov511->curframe >= 0) {
-		len = ov511_move_data(ov511, urb);
+		if (ov511->bridge == BRG_OV511 || 
+		    ov511->bridge == BRG_OV511PLUS)
+			len = ov511_move_data(ov511, urb);
+		else if (ov511->bridge == BRG_OV518 ||
+			 ov511->bridge == BRG_OV518PLUS)
+			len = ov518_move_data(ov511, urb);
+		else
+			err("Unknown bridge device (%d)", ov511->bridge);
 	} else if (waitqueue_active(&ov511->wq)) {
 		wake_up_interruptible(&ov511->wq);
 	}
 
-	/* Move to the next sbuf */
-	ov511->cursbuf = (ov511->cursbuf + 1) % OV511_NUMSBUF;
-
 	urb->dev = ov511->dev;
 
 	return;
 }
 
-static int ov511_init_isoc(struct usb_ov511 *ov511)
+/****************************************************************************
+ *
+ * Stream initialization and termination
+ *
+ ***************************************************************************/
+
+static int 
+ov511_init_isoc(struct usb_ov511 *ov511)
 {
 	urb_t *urb;
 	int fx, err, n, size;
 
 	PDEBUG(3, "*** Initializing capture ***");
 
-	ov511->compress = 0;
 	ov511->curframe = -1;
-	ov511->cursbuf = 0;
-	ov511->scratchlen = 0;
 
-	if (ov511->bridge == BRG_OV511)
-		if (cams == 1)			 size = 993;
-		else if (cams == 2)		 size = 513;
-		else if (cams == 3 || cams == 4) size = 257;
+	if (ov511->bridge == BRG_OV511) {
+		if (cams == 1)				size = 993;
+		else if (cams == 2)			size = 513;
+		else if (cams == 3 || cams == 4)	size = 257;
 		else {
 			err("\"cams\" parameter too high!");
 			return -1;
 		}
-	else if (ov511->bridge == BRG_OV511PLUS)
-		if (cams == 1)                      size = 961;
-		else if (cams == 2)                 size = 513;
-		else if (cams == 3 || cams == 4) size = 257;
-		else if (cams >= 5 && cams <= 8)    size = 129;
-		else if (cams >= 9 && cams <= 31)   size = 33;
+	} else if (ov511->bridge == BRG_OV511PLUS) {
+		if (cams == 1)				size = 961;
+		else if (cams == 2)			size = 513;
+		else if (cams == 3 || cams == 4)	size = 257;
+		else if (cams >= 5 && cams <= 8)	size = 129;
+		else if (cams >= 9 && cams <= 31)	size = 33;
 		else {
 			err("\"cams\" parameter too high!");
 			return -1;
 		}
-	else {
+	} else if (ov511->bridge == BRG_OV518 ||
+		   ov511->bridge == BRG_OV518PLUS) {
+		if (cams == 1)				size = 896;
+		else if (cams == 2)			size = 512;
+		else if (cams == 3 || cams == 4)	size = 256;
+		else if (cams >= 5 && cams <= 8)	size = 128;
+		else {
+			err("\"cams\" parameter too high!");
+			return -1;
+		}
+	} else {
 		err("invalid bridge type");
 		return -1;
 	}
 
-	ov511_set_packet_size(ov511, size);
+	if (packetsize == -1) {
+		// FIXME: OV518 is hardcoded to 15 FPS (alternate 5) for now
+		if (ov511->bridge == BRG_OV518 ||
+		    ov511->bridge == BRG_OV518PLUS)
+			ov511_set_packet_size(ov511, 640);
+		else
+			ov511_set_packet_size(ov511, size);
+	} else {
+			info("Forcing packet size to %d", packetsize);
+			ov511_set_packet_size(ov511, packetsize);
+	}
 
 	for (n = 0; n < OV511_NUMSBUF; n++) {
 		urb = usb_alloc_urb(FRAMES_PER_DESC);
@@ -1946,13 +4436,17 @@
 		urb->transfer_buffer = ov511->sbuf[n].data;
 		urb->complete = ov511_isoc_irq;
 		urb->number_of_packets = FRAMES_PER_DESC;
-		urb->transfer_buffer_length = ov511->packet_size * FRAMES_PER_DESC;
+		urb->transfer_buffer_length =
+		 ov511->packet_size * FRAMES_PER_DESC;
 		for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
-			urb->iso_frame_desc[fx].offset = ov511->packet_size * fx;
+			urb->iso_frame_desc[fx].offset = 
+			 ov511->packet_size * fx;
 			urb->iso_frame_desc[fx].length = ov511->packet_size;
 		}
 	}
 
+	ov511->streaming = 1;
+
 	ov511->sbuf[OV511_NUMSBUF - 1].urb->next = ov511->sbuf[0].urb;
 	for (n = 0; n < OV511_NUMSBUF - 1; n++)
 		ov511->sbuf[n].urb->next = ov511->sbuf[n+1].urb;
@@ -1964,19 +4458,18 @@
 			err("init isoc: usb_submit_urb(%d) ret %d", n, err);
 	}
 
-	ov511->streaming = 1;
-
 	return 0;
 }
 
-static void ov511_stop_isoc(struct usb_ov511 *ov511)
+static void 
+ov511_stop_isoc(struct usb_ov511 *ov511)
 {
 	int n;
 
 	if (!ov511->streaming || !ov511->dev)
 		return;
 
-	PDEBUG (3, "*** Stopping capture ***");
+	PDEBUG(3, "*** Stopping capture ***");
 
 	ov511_set_packet_size(ov511, 0);
 
@@ -1993,9 +4486,11 @@
 	}
 }
 
-static int ov511_new_frame(struct usb_ov511 *ov511, int framenum)
+static int 
+ov511_new_frame(struct usb_ov511 *ov511, int framenum)
 {
 	struct ov511_frame *frame;
+	int newnum;
 
 	PDEBUG(4, "ov511->curframe = %d, framenum = %d", ov511->curframe,
 		framenum);
@@ -2005,19 +4500,19 @@
 	/* If we're not grabbing a frame right now and the other frame is */
 	/* ready to be grabbed into, then use it instead */
 	if (ov511->curframe == -1) {
-		if (ov511->frame[(framenum - 1 + OV511_NUMFRAMES) % OV511_NUMFRAMES].grabstate == FRAME_READY)
-			framenum = (framenum - 1 + OV511_NUMFRAMES) % OV511_NUMFRAMES;
+		newnum = (framenum - 1 + OV511_NUMFRAMES) % OV511_NUMFRAMES;
+		if (ov511->frame[newnum].grabstate == FRAME_READY)
+			framenum = newnum;
 	} else
 		return 0;
 
 	frame = &ov511->frame[framenum];
 
-	PDEBUG (4, "framenum = %d, width = %d, height = %d", framenum, 
-		frame->width, frame->height);
+	PDEBUG(4, "framenum = %d, width = %d, height = %d", framenum, 
+	       frame->width, frame->height);
 
 	frame->grabstate = FRAME_GRABBING;
 	frame->scanstate = STATE_SCANNING;
-	frame->scanlength = 0;		/* accumulated in ov511_parse_data() */
 	frame->snapshot = 0;
 
 	ov511->curframe = framenum;
@@ -2041,9 +4536,12 @@
  * Buffer management
  *
  ***************************************************************************/
-static int ov511_alloc(struct usb_ov511 *ov511)
+static int 
+ov511_alloc(struct usb_ov511 *ov511)
 {
 	int i;
+	int w = ov511->maxwidth;
+	int h = ov511->maxheight;
 
 	PDEBUG(4, "entered");
 	down(&ov511->buf_lock);
@@ -2056,15 +4554,29 @@
 	if (ov511->buf_state == BUF_ALLOCATED)
 		goto out;
 
-	ov511->fbuf = rvmalloc(OV511_NUMFRAMES * MAX_DATA_SIZE);
+	ov511->fbuf = rvmalloc(OV511_NUMFRAMES * MAX_DATA_SIZE(w, h));
 	if (!ov511->fbuf)
 		goto error;
 
-	for (i = 0; i < OV511_NUMFRAMES; i++) {
-		ov511->frame[i].grabstate = FRAME_UNUSED;
-		ov511->frame[i].data = ov511->fbuf + i * MAX_DATA_SIZE;
-		PDEBUG(4, "frame[%d] @ %p", i, ov511->frame[i].data);
+	ov511->rawfbuf = vmalloc(OV511_NUMFRAMES * MAX_RAW_DATA_SIZE(w, h));
+	if (!ov511->rawfbuf) {
+		rvfree(ov511->fbuf, OV511_NUMFRAMES * MAX_DATA_SIZE(w, h));
+		ov511->fbuf = NULL;
+		goto error;
+	}
+	memset(ov511->rawfbuf, 0, OV511_NUMFRAMES * MAX_RAW_DATA_SIZE(w, h));
 
+	ov511->tempfbuf = vmalloc(OV511_NUMFRAMES * MAX_RAW_DATA_SIZE(w, h));
+	if (!ov511->tempfbuf) {
+		vfree(ov511->rawfbuf);
+		ov511->rawfbuf = NULL;
+		rvfree(ov511->fbuf, OV511_NUMFRAMES * MAX_DATA_SIZE(w, h));
+		ov511->fbuf = NULL;
+		goto error;
+	}
+	memset(ov511->tempfbuf, 0, OV511_NUMFRAMES * MAX_RAW_DATA_SIZE(w, h));
+
+	for (i = 0; i < OV511_NUMSBUF; i++) {
 		ov511->sbuf[i].data = kmalloc(FRAMES_PER_DESC *
 			MAX_FRAME_SIZE_PER_DESC, GFP_KERNEL);
 		if (!ov511->sbuf[i].data) {
@@ -2072,12 +4584,28 @@
 				kfree(ov511->sbuf[i].data);
 				ov511->sbuf[i].data = NULL;
 			}
-			rvfree(ov511->fbuf, OV511_NUMFRAMES * MAX_DATA_SIZE);
+			vfree(ov511->tempfbuf);
+			ov511->tempfbuf = NULL;
+			vfree(ov511->rawfbuf);
+			ov511->rawfbuf = NULL;
+			rvfree(ov511->fbuf,
+			       OV511_NUMFRAMES * MAX_DATA_SIZE(w, h));
 			ov511->fbuf = NULL;
+
 			goto error;
 		}
 		PDEBUG(4, "sbuf[%d] @ %p", i, ov511->sbuf[i].data);
 	}
+
+	for (i = 0; i < OV511_NUMFRAMES; i++) {
+		ov511->frame[i].data = ov511->fbuf + i * MAX_DATA_SIZE(w, h);
+		ov511->frame[i].rawdata = ov511->rawfbuf 
+		 + i * MAX_RAW_DATA_SIZE(w, h);
+		ov511->frame[i].tempdata = ov511->tempfbuf 
+		 + i * MAX_RAW_DATA_SIZE(w, h);
+		PDEBUG(4, "frame[%d] @ %p", i, ov511->frame[i].data);
+	}
+
 	ov511->buf_state = BUF_ALLOCATED;
 out:
 	up(&ov511->buf_lock);
@@ -2095,29 +4623,48 @@
  * - Because this code will free any non-null pointer, you must be sure to null
  *   them if you explicitly free them somewhere else!
  */
-static void ov511_do_dealloc(struct usb_ov511 *ov511)
+static void 
+ov511_do_dealloc(struct usb_ov511 *ov511)
 {
 	int i;
 	PDEBUG(4, "entered");
 
 	if (ov511->fbuf) {
-		rvfree(ov511->fbuf, OV511_NUMFRAMES * MAX_DATA_SIZE);
+		rvfree(ov511->fbuf, OV511_NUMFRAMES
+		       * MAX_DATA_SIZE(ov511->maxwidth, ov511->maxheight));
 		ov511->fbuf = NULL;
 	}
 
-	for (i = 0; i < OV511_NUMFRAMES; i++) {
+	if (ov511->rawfbuf) {
+		vfree(ov511->rawfbuf);
+		ov511->rawfbuf = NULL;
+	}
+
+	if (ov511->tempfbuf) {
+		vfree(ov511->tempfbuf);
+		ov511->tempfbuf = NULL;
+	}
+
+	for (i = 0; i < OV511_NUMSBUF; i++) {
 		if (ov511->sbuf[i].data) {
 			kfree(ov511->sbuf[i].data);
 			ov511->sbuf[i].data = NULL;
 		}
 	}
 
+	for (i = 0; i < OV511_NUMFRAMES; i++) {
+		ov511->frame[i].data = NULL;
+		ov511->frame[i].rawdata = NULL;
+		ov511->frame[i].tempdata = NULL;
+	}
+
 	PDEBUG(4, "buffer memory deallocated");
 	ov511->buf_state = BUF_NOT_ALLOCATED;
 	PDEBUG(4, "leaving");
 }
 
-static void ov511_buf_callback(unsigned long data)
+static void 
+ov511_buf_callback(unsigned long data)
 {
 	struct usb_ov511 *ov511 = (struct usb_ov511 *)data;
 	PDEBUG(4, "entered");
@@ -2130,7 +4677,8 @@
 	PDEBUG(4, "leaving");
 }
 
-static void ov511_dealloc(struct usb_ov511 *ov511, int now)
+static void 
+ov511_dealloc(struct usb_ov511 *ov511, int now)
 {
 	struct timer_list *bt = &(ov511->buf_timer);
 	PDEBUG(4, "entered");
@@ -2163,13 +4711,14 @@
  *
  ***************************************************************************/
 
-static int ov511_open(struct video_device *dev, int flags)
+static int 
+ov511_open(struct video_device *vdev, int flags)
 {
-	struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
-	int err;
+	struct usb_ov511 *ov511 = vdev->priv;
+	int err, i;
 
-	MOD_INC_USE_COUNT;
 	PDEBUG(4, "opening");
+
 	down(&ov511->lock);
 
 	err = -EBUSY;
@@ -2182,6 +4731,24 @@
 
 	ov511->sub_flag = 0;
 
+	/* In case app doesn't set them... */
+	if (ov51x_set_default_params(ov511) < 0)
+		goto out;
+
+	/* Make sure frames are reset */
+	for (i = 0; i < OV511_NUMFRAMES; i++) {
+		ov511->frame[i].grabstate = FRAME_UNUSED;
+		ov511->frame[i].bytes_read = 0;
+	}
+
+	/* If compression is on, make sure now that a 
+	 * decompressor can be loaded */
+	if (ov511->compress && !ov511->decomp_ops) {
+		err = ov51x_request_decompressor(ov511);
+		if (err)
+			goto out;
+	}
+
 	err = ov511_init_isoc(ov511);
 	if (err) {
 		ov511_dealloc(ov511, 0);
@@ -2189,17 +4756,18 @@
 	}
 
 	ov511->user++;
+	
+	if (ov511->led_policy == LED_AUTO)
+		ov51x_led_control(ov511, 1);
 
 out:
 	up(&ov511->lock);
 
-	if (err)
-		MOD_DEC_USE_COUNT;
-
 	return err;
 }
 
-static void ov511_close(struct video_device *dev)
+static void 
+ov511_close(struct video_device *dev)
 {
 	struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
 
@@ -2210,40 +4778,50 @@
 	ov511->user--;
 	ov511_stop_isoc(ov511);
 
+	ov51x_release_decompressor(ov511);
+
+	if (ov511->led_policy == LED_AUTO)
+		ov51x_led_control(ov511, 0);
+
 	if (ov511->dev)
 		ov511_dealloc(ov511, 0);
 
 	up(&ov511->lock);
 
+	/* Device unplugged while open. Only a minimum of unregistration is done
+	 * here; the disconnect callback already did the rest. */
 	if (!ov511->dev) {
 		ov511_dealloc(ov511, 1);
 		video_unregister_device(&ov511->vdev);
 		kfree(ov511);
 		ov511 = NULL;
 	}
-
-	MOD_DEC_USE_COUNT;
 }
 
-static int ov511_init_done(struct video_device *dev)
+static int 
+ov511_init_done(struct video_device *vdev)
 {
 #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
-	create_proc_ov511_cam((struct usb_ov511 *)dev);
+	create_proc_ov511_cam((struct usb_ov511 *)vdev);
 #endif
 
 	return 0;
 }
 
-static long ov511_write(struct video_device *dev, const char *buf, unsigned long count, int noblock)
+static long 
+ov511_write(struct video_device *vdev, const char *buf,
+	    unsigned long count, int noblock)
 {
 	return -EINVAL;
 }
 
-static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
+/* Do not call this function directly! */
+static int 
+ov511_ioctl_internal(struct video_device *vdev, unsigned int cmd, void *arg)
 {
 	struct usb_ov511 *ov511 = (struct usb_ov511 *)vdev;
 
-	PDEBUG(4, "IOCtl: 0x%X", cmd);
+	PDEBUG(5, "IOCtl: 0x%X", cmd);
 
 	if (!ov511->dev)
 		return -EIO;	
@@ -2253,17 +4831,24 @@
 	{
 		struct video_capability b;
 
-		PDEBUG (4, "VIDIOCGCAP");
+		PDEBUG(4, "VIDIOCGCAP");
 
 		memset(&b, 0, sizeof(b));
-		strcpy(b.name, "OV511 USB Camera");
+		sprintf(b.name, "%s USB Camera",
+			ov511->bridge == BRG_OV511 ? "OV511" :
+			ov511->bridge == BRG_OV511PLUS ? "OV511+" :
+			ov511->bridge == BRG_OV518 ? "OV518" :
+			ov511->bridge == BRG_OV518PLUS ? "OV518+" :
+			"unknown");
 		b.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE;
-		b.channels = 1;
-		b.audios = 0;
+		if (ov511->has_tuner)
+			b.type |= VID_TYPE_TUNER;
+		b.channels = ov511->num_inputs;
+		b.audios = ov511->has_audio_proc ? 1:0;
 		b.maxwidth = ov511->maxwidth;
 		b.maxheight = ov511->maxheight;
-		b.minwidth = 160;
-		b.minheight = 120;
+		b.minwidth = ov511->minwidth;
+		b.minheight = ov511->minheight;
 
 		if (copy_to_user(arg, &b, sizeof(b)))
 			return -EFAULT;
@@ -2274,15 +4859,23 @@
 	{
 		struct video_channel v;
 
+		PDEBUG(4, "VIDIOCGCHAN");
+
 		if (copy_from_user(&v, arg, sizeof(v)))
 			return -EFAULT;
-		if (v.channel != 0)
+
+		if ((unsigned)(v.channel) >= ov511->num_inputs) {
+			err("Invalid channel (%d)", v.channel);
 			return -EINVAL;
+		}
 
-		v.flags = 0;
-		v.tuners = 0;
-		v.type = VIDEO_TYPE_CAMERA;
-		strcpy(v.name, "Camera");
+		v.norm = ov511->norm;
+		v.type = (ov511->has_tuner) ? VIDEO_TYPE_TV : VIDEO_TYPE_CAMERA;
+		v.flags = (ov511->has_tuner) ? VIDEO_VC_TUNER : 0;
+		v.flags |= (ov511->has_audio_proc) ? VIDEO_VC_AUDIO : 0;
+//		v.flags |= (ov511->has_decoder) ? VIDEO_VC_NORM : 0;
+		v.tuners = (ov511->has_tuner) ? 1:0;
+		decoder_get_input_name(ov511, v.channel, v.name);
 
 		if (copy_to_user(arg, &v, sizeof(v)))
 			return -EFAULT;
@@ -2291,13 +4884,42 @@
 	}
 	case VIDIOCSCHAN:
 	{
-		int v;
+		struct video_channel v;
+		int err;
+
+		PDEBUG(4, "VIDIOCSCHAN");
 
 		if (copy_from_user(&v, arg, sizeof(v)))
 			return -EFAULT;
 
-		if (v != 0)
+		/* Make sure it's not a camera */
+		if (!ov511->has_decoder) {
+			if (v.channel == 0)
+				return 0;
+			else
+				return -EINVAL;
+		}
+
+		if (v.norm != VIDEO_MODE_PAL &&
+		    v.norm != VIDEO_MODE_NTSC &&
+		    v.norm != VIDEO_MODE_SECAM &&
+		    v.norm != VIDEO_MODE_AUTO) {
+			err("Invalid norm (%d)", v.norm);
+			return -EINVAL;
+		}
+
+		if ((unsigned)(v.channel) >= ov511->num_inputs) {
+			err("Invalid channel (%d)", v.channel);
 			return -EINVAL;
+		}
+
+		err = decoder_set_input(ov511, v.channel);
+		if (err)
+			return err;
+
+		err = decoder_set_norm(ov511, v.norm);
+		if (err)
+			return err;
 
 		return 0;
 	}
@@ -2305,11 +4927,11 @@
 	{
 		struct video_picture p;
 
-		PDEBUG (4, "VIDIOCGPICT");
+		PDEBUG(4, "VIDIOCGPICT");
 
 		memset(&p, 0, sizeof(p));
 
-		if (ov7610_get_picture(ov511, &p))
+		if (sensor_get_picture(ov511, &p))
 			return -EIO;
 
 		if (copy_to_user(arg, &p, sizeof(p)))
@@ -2322,22 +4944,40 @@
 		struct video_picture p;
 		int i;
 
-		PDEBUG (4, "VIDIOCSPICT");
+		PDEBUG(4, "VIDIOCSPICT");
 
 		if (copy_from_user(&p, arg, sizeof(p)))
 			return -EFAULT;
 
 		if (!ov511_get_depth(p.palette))
 			return -EINVAL;
-			
-		if (ov7610_set_picture(ov511, &p))
+
+		if (sensor_set_picture(ov511, &p))
 			return -EIO;
 
+		if (force_palette && p.palette != force_palette) {
+			info("Palette rejected (%d)", p.palette);
+			return -EINVAL;
+		}
+
+		// FIXME: Format should be independent of frames
+		if (p.palette != ov511->frame[0].format) {
+			PDEBUG(4, "Detected format change");
+
+			/* If we're collecting previous frame wait
+			   before changing modes */
+			interruptible_sleep_on(&ov511->wq);
+			if (signal_pending(current)) return -EINTR;
+
+			mode_init_regs(ov511, ov511->frame[0].width,
+				ov511->frame[0].height,	p.palette,
+				ov511->sub_flag);
+		}
+
 		PDEBUG(4, "Setting depth=%d, palette=%d", p.depth, p.palette);
 		for (i = 0; i < OV511_NUMFRAMES; i++) {
 			ov511->frame[i].depth = p.depth;
 			ov511->frame[i].format = p.palette;
-			ov511->frame[i].segsize = GET_SEGSIZE(p.palette);
 		}
 
 		return 0;
@@ -2346,7 +4986,7 @@
 	{
 		int vf;
 
-		PDEBUG (4, "VIDIOCGCAPTURE");
+		PDEBUG(4, "VIDIOCGCAPTURE");
 
 		if (copy_from_user(&vf, arg, sizeof(vf)))
 			return -EFAULT;
@@ -2357,6 +4997,8 @@
 	{
 		struct video_capture vc;
 
+		PDEBUG(4, "VIDIOCSCAPTURE");
+
 		if (copy_from_user(&vc, arg, sizeof(vc)))
 			return -EFAULT;
 		if (vc.flags)
@@ -2391,7 +5033,7 @@
 		if (copy_from_user(&vw, arg, sizeof(vw)))
 			return -EFAULT;
 
-		PDEBUG (4, "VIDIOCSWIN: width=%d, height=%d",
+		PDEBUG(4, "VIDIOCSWIN: width=%d, height=%d",
 			vw.width, vw.height);
 
 #if 0
@@ -2410,7 +5052,7 @@
 		interruptible_sleep_on(&ov511->wq);
 		if (signal_pending(current)) return -EINTR;
 
-		result = ov511_mode_init_regs(ov511, vw.width, vw.height,
+		result = mode_init_regs(ov511, vw.width, vw.height,
 			ov511->frame[0].format, ov511->sub_flag);
 		if (result < 0)
 			return result;
@@ -2433,7 +5075,7 @@
 		vw.height = ov511->frame[0].height;
 		vw.flags = 30;
 
-		PDEBUG (4, "VIDIOCGWIN: %dx%d", vw.width, vw.height);
+		PDEBUG(4, "VIDIOCGWIN: %dx%d", vw.width, vw.height);
 
 		if (copy_to_user(arg, &vw, sizeof(vw)))
 			return -EFAULT;
@@ -2444,14 +5086,18 @@
 	{
 		struct video_mbuf vm;
 		int i;
-		
+
+		PDEBUG(4, "VIDIOCGMBUF");
+
 		memset(&vm, 0, sizeof(vm));
-		vm.size = OV511_NUMFRAMES * MAX_DATA_SIZE;
+		vm.size = OV511_NUMFRAMES
+			* MAX_DATA_SIZE(ov511->maxwidth, ov511->maxheight);
 		vm.frames = OV511_NUMFRAMES;
+
 		vm.offsets[0] = 0;
 		for (i = 1; i < OV511_NUMFRAMES; i++) {
-			vm.offsets[i] = vm.offsets[i-1] + MAX_FRAME_SIZE
-				+ sizeof (struct timeval);
+			vm.offsets[i] = vm.offsets[i-1]
+			   + MAX_DATA_SIZE(ov511->maxwidth, ov511->maxheight);
 		}
 
 		if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
@@ -2482,63 +5128,74 @@
 			return -EINVAL;
 		}
 
-		if (vm.width > ov511->maxwidth || vm.height > ov511->maxheight) {
+		if (vm.width > ov511->maxwidth 
+		    || vm.height > ov511->maxheight) {
 			err("VIDIOCMCAPTURE: requested dimensions too big");
 			return -EINVAL;
 		}
 
-		if (ov511->frame[vm.frame].grabstate == FRAME_GRABBING)
+		if (ov511->frame[vm.frame].grabstate == FRAME_GRABBING) {
+			PDEBUG(4, "VIDIOCMCAPTURE: already grabbing");
 			return -EBUSY;
+		}
+
+		if (force_palette && vm.format != force_palette) {
+			info("palette rejected (%d)", vm.format);
+			return -EINVAL;
+		}
 
-		/* Don't compress if the size changed */
 		if ((ov511->frame[vm.frame].width != vm.width) ||
 		    (ov511->frame[vm.frame].height != vm.height) ||
 		    (ov511->frame[vm.frame].format != vm.format) ||
-		    (ov511->frame[vm.frame].sub_flag !=
-		     ov511->sub_flag)) {
+		    (ov511->frame[vm.frame].sub_flag != ov511->sub_flag) ||
+		    (ov511->frame[vm.frame].depth != depth)) {
+			PDEBUG(4, "VIDIOCMCAPTURE: change in image parameters");
+
 			/* If we're collecting previous frame wait
 			   before changing modes */
 			interruptible_sleep_on(&ov511->wq);
 			if (signal_pending(current)) return -EINTR;
-			ret = ov511_mode_init_regs(ov511, vm.width, vm.height,
+			ret = mode_init_regs(ov511, vm.width, vm.height,
 				vm.format, ov511->sub_flag);
 #if 0
-			if (ret < 0)
+			if (ret < 0) {
+				PDEBUG(1, "Got error while initializing regs ");
 				return ret;
+			}
 #endif
+			ov511->frame[vm.frame].width = vm.width;
+			ov511->frame[vm.frame].height = vm.height;
+			ov511->frame[vm.frame].format = vm.format;
+			ov511->frame[vm.frame].sub_flag = ov511->sub_flag;
+			ov511->frame[vm.frame].depth = depth;
 		}
 
-		ov511->frame[vm.frame].width = vm.width;
-		ov511->frame[vm.frame].height = vm.height;
-		ov511->frame[vm.frame].format = vm.format;
-		ov511->frame[vm.frame].sub_flag = ov511->sub_flag;
-		ov511->frame[vm.frame].segsize = GET_SEGSIZE(vm.format);
-		ov511->frame[vm.frame].depth = depth;
-
 		/* Mark it as ready */
 		ov511->frame[vm.frame].grabstate = FRAME_READY;
 
+		PDEBUG(4, "VIDIOCMCAPTURE: renewing frame %d", vm.frame);
+
 		return ov511_new_frame(ov511, vm.frame);
 	}
 	case VIDIOCSYNC:
 	{
-		int frame;
+		int fnum, rc;
+		struct ov511_frame *frame;
 
-		if (copy_from_user((void *)&frame, arg, sizeof(int)))
+		if (copy_from_user((void *)&fnum, arg, sizeof(int)))
 			return -EFAULT;
 
-		if ((unsigned)frame >= OV511_NUMFRAMES) {
-			err("VIDIOCSYNC: invalid frame (%d)", frame);
+		if ((unsigned)fnum >= OV511_NUMFRAMES) {
+			err("VIDIOCSYNC: invalid frame (%d)", fnum);
 			return -EINVAL;
 		}
 
-		PDEBUG(4, "syncing to frame %d, grabstate = %d", frame,
-		       ov511->frame[frame].grabstate);
+		frame = &ov511->frame[fnum];
 
-		if(frame < 0 || frame >= OV511_NUMFRAMES)
-			return -EINVAL;
-			
-		switch (ov511->frame[frame].grabstate) {
+		PDEBUG(4, "syncing to frame %d, grabstate = %d", fnum,
+		       frame->grabstate);
+
+		switch (frame->grabstate) {
 		case FRAME_UNUSED:
 			return -EINVAL;
 		case FRAME_READY:
@@ -2548,56 +5205,41 @@
 			if (!ov511->dev)
 				return -EIO;
 
-			do {
-#if 0
-				init_waitqueue_head(&ov511->frame[frame].wq);
-#endif
-				interruptible_sleep_on(&ov511->frame[frame].wq);
-				if (signal_pending(current)) {
-					if (retry_sync) {
-						PDEBUG(3, "***retry sync***");
-
-						/* Polling apps will destroy frames with that! */
-						ov511_new_frame(ov511, frame);
-						ov511->curframe = -1;
-
-						/* This will request another frame. */
-						if (waitqueue_active(&ov511->frame[frame].wq))
-							wake_up_interruptible(&ov511->frame[frame].wq);
-
-						return 0;
- 					} else {
-						return -EINTR;
-					}
-				}
-			} while (ov511->frame[frame].grabstate == FRAME_GRABBING);
+			rc = wait_event_interruptible(frame->wq,
+			    (frame->grabstate == FRAME_DONE)
+			    || (frame->grabstate == FRAME_ERROR));
+
+			if (rc)
+				return rc;
 
-			if (ov511->frame[frame].grabstate == FRAME_ERROR) {
+			if (frame->grabstate == FRAME_ERROR) {
 				int ret;
 
-				if ((ret = ov511_new_frame(ov511, frame)) < 0)
+				if ((ret = ov511_new_frame(ov511, fnum)) < 0)
 					return ret;
 				goto redo;
-			}			
+			}
+			/* Fall through */			
 		case FRAME_DONE:
-			if (ov511->snap_enabled && !ov511->frame[frame].snapshot) {
+			if (ov511->snap_enabled && !frame->snapshot) {
 				int ret;
-				if ((ret = ov511_new_frame(ov511, frame)) < 0)
+				if ((ret = ov511_new_frame(ov511, fnum)) < 0)
 					return ret;
 				goto redo;
 			}
 
-			ov511->frame[frame].grabstate = FRAME_UNUSED;
+			frame->grabstate = FRAME_UNUSED;
 
 			/* Reset the hardware snapshot button */
 			/* FIXME - Is this the best place for this? */
-			if ((ov511->snap_enabled) &&
-			    (ov511->frame[frame].snapshot)) {
-				ov511->frame[frame].snapshot = 0;
-				ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01);
-				ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x03);
-				ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01);
+			if ((ov511->snap_enabled) && (frame->snapshot)) {
+				frame->snapshot = 0;
+				ov51x_clear_snapshot(ov511);
 			}
+
+			/* Decompression, format conversion, etc... */
+			ov511_postprocess(ov511, frame);
+
 			break;
 		} /* end switch */
 
@@ -2607,6 +5249,8 @@
 	{
 		struct video_buffer vb;
 
+		PDEBUG(4, "VIDIOCSCHAN");
+
 		memset(&vb, 0, sizeof(vb));
 		vb.base = NULL;	/* frame buffer not supported, not used */
 
@@ -2615,43 +5259,170 @@
 
 		return 0;
 	}
-	case VIDIOCKEY:
+	case VIDIOCGUNIT:
+	{
+		struct video_unit vu;
+
+		PDEBUG(4, "VIDIOCGUNIT");
+
+		memset(&vu, 0, sizeof(vu));
+
+		vu.video = ov511->vdev.minor;	/* Video minor */
+		vu.vbi = VIDEO_NO_UNIT;		/* VBI minor */
+		vu.radio = VIDEO_NO_UNIT;	/* Radio minor */
+		vu.audio = VIDEO_NO_UNIT;	/* Audio minor */
+		vu.teletext = VIDEO_NO_UNIT;	/* Teletext minor */
+
+		if (copy_to_user((void *)arg, (void *)&vu, sizeof(vu)))
+			return -EFAULT;
+
 		return 0;
-	case VIDIOCCAPTURE:
-		return -EINVAL;
-	case VIDIOCSFBUF:
-		return -EINVAL;
+	}
 	case VIDIOCGTUNER:
+	{
+		struct video_tuner v;
+
+		PDEBUG(4, "VIDIOCGTUNER");
+
+		if (copy_from_user(&v, arg, sizeof(v)))
+			return -EFAULT;
+
+		if (!ov511->has_tuner || v.tuner)	// Only tuner 0
+			return -EINVAL;
+
+		strcpy(v.name, "Television");
+
+		// FIXME: Need a way to get the real values
+		v.rangelow = 0;
+		v.rangehigh = ~0;
+
+		v.flags = VIDEO_TUNER_PAL | VIDEO_TUNER_NTSC |
+		    VIDEO_TUNER_SECAM;
+		v.mode = 0; 		/* FIXME:  Not sure what this is yet */
+		v.signal = 0xFFFF;	/* unknown */
+
+		call_i2c_clients(ov511, cmd, &v);
+
+		if (copy_to_user(arg, &v, sizeof(v)))
+			return -EFAULT;
+
+		return 0;
+	}
 	case VIDIOCSTUNER:
-		return -EINVAL;
+	{
+		struct video_tuner v;
+		int err;
+
+		PDEBUG(4, "VIDIOCSTUNER");
+
+		if (copy_from_user(&v, arg, sizeof(v)))
+			return -EFAULT;
+
+		/* Only no or one tuner for now */
+		if (!ov511->has_tuner || v.tuner)
+			return -EINVAL;
+
+		/* and it only has certain valid modes */
+		if (v.mode != VIDEO_MODE_PAL &&
+		    v.mode != VIDEO_MODE_NTSC &&
+		    v.mode != VIDEO_MODE_SECAM) return -EOPNOTSUPP;
+
+		/* Is this right/necessary? */
+		err = decoder_set_norm(ov511, v.mode);
+		if (err)
+			return err;
+
+		call_i2c_clients(ov511, cmd, &v);
+
+		return 0;
+	}
 	case VIDIOCGFREQ:
+	{
+		unsigned long v = ov511->freq;
+
+		PDEBUG(4, "VIDIOCGFREQ");
+
+		if (!ov511->has_tuner)
+			return -EINVAL;
+#if 0
+		/* FIXME: this is necessary for testing */
+		v = 46*16;
+#endif
+		if (copy_to_user(arg, &v, sizeof(v)))
+			return -EFAULT;
+
+		return 0;
+	}
 	case VIDIOCSFREQ:
-		return -EINVAL;
+	{
+		unsigned long v;
+
+		if (!ov511->has_tuner)
+			return -EINVAL;
+
+		if (copy_from_user(&v, arg, sizeof(v)))
+			return -EFAULT;
+
+		PDEBUG(4, "VIDIOCSFREQ: %lx", v);
+
+		ov511->freq = v;
+		call_i2c_clients(ov511, cmd, &v);
+
+		return 0;
+	}
 	case VIDIOCGAUDIO:
 	case VIDIOCSAUDIO:
-		return -EINVAL;
+	{
+		/* FIXME: Implement this... */
+		return 0;
+	}
 	default:
+		PDEBUG(3, "Unsupported IOCtl: 0x%X", cmd);
 		return -ENOIOCTLCMD;
 	} /* end switch */
 
 	return 0;
 }
 
-static long ov511_read(struct video_device *dev, char *buf, unsigned long count, int noblock)
+static int 
+ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
 {
-	struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
-	int i;
-	int frmx = -1;
-	volatile struct ov511_frame *frame;
+	int rc;
+	struct usb_ov511 *ov511 = vdev->priv;
+
+	if (down_interruptible(&ov511->lock))
+		return -EINTR;
+
+	rc = ov511_ioctl_internal(vdev, cmd, arg);
+
+	up(&ov511->lock);
+	return rc;
+}
+
+static inline long 
+ov511_read(struct video_device *vdev, char *buf, unsigned long count,
+	   int noblock)
+{
+	struct usb_ov511 *ov511 = vdev->priv;
+	int i, rc = 0, frmx = -1;
+	struct ov511_frame *frame;
+
+	if (down_interruptible(&ov511->lock))
+		return -EINTR;
 
 	PDEBUG(4, "%ld bytes, noblock=%d", count, noblock);
 
-	if (!dev || !buf)
-		return -EFAULT;
+	if (!vdev || !buf) {
+		rc = -EFAULT;
+		goto error;
+	}
 
-	if (!ov511->dev)
-		return -EIO;
+	if (!ov511->dev) {
+		rc = -EIO;
+		goto error;
+	}
 
+// FIXME: Only supports two frames
 	/* See if a frame is completed, then use it. */
 	if (ov511->frame[0].grabstate >= FRAME_DONE)	/* _DONE or _ERROR */
 		frmx = 0;
@@ -2659,8 +5430,10 @@
 		frmx = 1;
 
 	/* If nonblocking we return immediately */
-	if (noblock && (frmx == -1))
-		return -EAGAIN;
+	if (noblock && (frmx == -1)) {
+		rc = -EAGAIN;
+		goto error;
+	}
 
 	/* If no FRAME_DONE, look for a FRAME_GRABBING state. */
 	/* See if a frame is in process (grabbing), then use it. */
@@ -2672,89 +5445,116 @@
 	}
 
 	/* If no frame is active, start one. */
-	if (frmx == -1)
-		ov511_new_frame(ov511, frmx = 0);
+	if (frmx == -1) {
+		if ((rc = ov511_new_frame(ov511, frmx = 0))) {
+			err("read: ov511_new_frame error");
+			goto error;
+		}
+	}
 
 	frame = &ov511->frame[frmx];
 
 restart:
-	if (!ov511->dev)
-		return -EIO;
+	if (!ov511->dev) {
+		rc = -EIO;
+		goto error;
+	}
 
 	/* Wait while we're grabbing the image */
 	PDEBUG(4, "Waiting image grabbing");
-	while (frame->grabstate == FRAME_GRABBING) {
-		interruptible_sleep_on(&ov511->frame[frmx].wq);
-		if (signal_pending(current))
-			return -EINTR;
-	}
+	rc = wait_event_interruptible(frame->wq, 
+		(frame->grabstate == FRAME_DONE)
+		|| (frame->grabstate == FRAME_ERROR));
+
+	if (rc)
+		goto error;
+
 	PDEBUG(4, "Got image, frame->grabstate = %d", frame->grabstate);
+	PDEBUG(4, "bytes_recvd = %d", frame->bytes_recvd);
 
 	if (frame->grabstate == FRAME_ERROR) {
 		frame->bytes_read = 0;
 		err("** ick! ** Errored frame %d", ov511->curframe);
-		if (ov511_new_frame(ov511, frmx))
+		if (ov511_new_frame(ov511, frmx)) {
 			err("read: ov511_new_frame error");
+			goto error;
+		}
 		goto restart;
 	}
 
 
 	/* Repeat until we get a snapshot frame */
 	if (ov511->snap_enabled)
-		PDEBUG (4, "Waiting snapshot frame");
+		PDEBUG(4, "Waiting snapshot frame");
 	if (ov511->snap_enabled && !frame->snapshot) {
 		frame->bytes_read = 0;
-		if (ov511_new_frame(ov511, frmx))
-			err("ov511_new_frame error");
+		if ((rc = ov511_new_frame(ov511, frmx))) {
+			err("read: ov511_new_frame error");
+			goto error;
+		}
 		goto restart;
 	}
 
 	/* Clear the snapshot */
 	if (ov511->snap_enabled && frame->snapshot) {
 		frame->snapshot = 0;
-		ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01);
-		ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x03);
-		ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01);
+		ov51x_clear_snapshot(ov511);
 	}
 
-	PDEBUG(4, "frmx=%d, bytes_read=%ld, scanlength=%ld", frmx,
-		frame->bytes_read, frame->scanlength);
+	/* Decompression, format conversion, etc... */
+	ov511_postprocess(ov511, frame);
+
+	PDEBUG(4, "frmx=%d, bytes_read=%ld, length=%ld", frmx,
+		frame->bytes_read,
+		get_frame_length(frame));
 
 	/* copy bytes to user space; we allow for partials reads */
-//	if ((count + frame->bytes_read) > frame->scanlength)
+//	if ((count + frame->bytes_read) 
+//	    > get_frame_length((struct ov511_frame *)frame))
 //		count = frame->scanlength - frame->bytes_read;
 
 	/* FIXME - count hardwired to be one frame... */
-	count = frame->width * frame->height * (frame->depth >> 3);
+	count = get_frame_length(frame);
 
 	PDEBUG(4, "Copy to user space: %ld bytes", count);
 	if ((i = copy_to_user(buf, frame->data + frame->bytes_read, count))) {
 		PDEBUG(4, "Copy failed! %d bytes not copied", i);
-		return -EFAULT;
+		rc = -EFAULT;
+		goto error;
 	}
 
 	frame->bytes_read += count;
 	PDEBUG(4, "{copy} count used=%ld, new bytes_read=%ld",
 		count, frame->bytes_read);
 
-	if (frame->bytes_read >= frame->scanlength) { /* All data has been read */
+	/* If all data has been read... */
+	if (frame->bytes_read
+	    >= get_frame_length(frame)) {
 		frame->bytes_read = 0;
 
+// FIXME: Only supports two frames
 		/* Mark it as available to be used again. */
 		ov511->frame[frmx].grabstate = FRAME_UNUSED;
-		if (ov511_new_frame(ov511, !frmx))
+		if ((rc = ov511_new_frame(ov511, !frmx))) {
 			err("ov511_new_frame returned error");
+			goto error;
+		}
 	}
 
 	PDEBUG(4, "read finished, returning %ld (sweet)", count);
 
+	up(&ov511->lock);
 	return count;
+
+error:
+	up(&ov511->lock);
+	return rc;
 }
 
-static int ov511_mmap(struct video_device *dev, const char *adr,
-	unsigned long size)
+static int 
+ov511_mmap(struct video_device *vdev, const char *adr, unsigned long size)
 {
-	struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
+	struct usb_ov511 *ov511 = vdev->priv;
 	unsigned long start = (unsigned long)adr;
 	unsigned long page, pos;
 
@@ -2763,14 +5563,21 @@
 
 	PDEBUG(4, "mmap: %ld (%lX) bytes", size, size);
 
-	if (size > (((OV511_NUMFRAMES * MAX_DATA_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))
+	if (size > (((OV511_NUMFRAMES
+	              * MAX_DATA_SIZE(ov511->maxwidth, ov511->maxheight)
+	              + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))))
 		return -EINVAL;
 
+	if (down_interruptible(&ov511->lock))
+		return -EINTR;
+
 	pos = (unsigned long)ov511->fbuf;
 	while (size > 0) {
 		page = kvirt_to_pa(pos);
-		if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED))
+		if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) {
+			up(&ov511->lock);
 			return -EAGAIN;
+		}
 		start += PAGE_SIZE;
 		pos += PAGE_SIZE;
 		if (size > PAGE_SIZE)
@@ -2779,10 +5586,12 @@
 			size = 0;
 	}
 
+	up(&ov511->lock);
 	return 0;
 }
 
 static struct video_device ov511_template = {
+	owner:		THIS_MODULE,
 	name:		"OV511 USB Camera",
 	type:		VID_TYPE_CAPTURE,
 	hardware:	VID_HARDWARE_OV511,
@@ -2795,15 +5604,237 @@
 	initialize:	ov511_init_done,
 };
 
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+static int 
+ov511_control_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+		    unsigned long ularg)
+{
+	struct proc_dir_entry *pde;
+	struct usb_ov511 *ov511;
+	void *arg = (void *) ularg;
+	int rc;
+
+	pde = (struct proc_dir_entry *) inode->u.generic_ip;
+	if (!pde)
+		return -ENOENT;
+
+	ov511 = (struct usb_ov511 *) pde->data;
+	if (!ov511)
+		return -ENODEV;
+
+	if (!ov511->dev)
+		return -EIO;
+
+	/* Should we pass through standard V4L IOCTLs? */
+
+	switch (cmd) {
+	case OV511IOC_GINTVER:
+	{
+		int ver = OV511_INTERFACE_VER;
+
+		PDEBUG(4, "Get interface version: %d", ver);
+		if (copy_to_user(arg, &ver, sizeof(ver)))
+			return -EFAULT;
+
+		return 0;
+	}
+	case OV511IOC_GUSHORT:
+	{
+		struct ov511_ushort_opt opt;
+
+		if (copy_from_user(&opt, arg, sizeof(opt)))
+			return -EFAULT;
+
+		switch (opt.optnum) {
+		case OV511_USOPT_BRIGHT:
+			rc = sensor_get_brightness(ov511, &(opt.val));
+			if (rc)	return rc;
+			break;
+		case OV511_USOPT_SAT:
+			rc = sensor_get_saturation(ov511, &(opt.val));
+			if (rc)	return rc;
+			break;
+		case OV511_USOPT_HUE:
+			rc = sensor_get_hue(ov511, &(opt.val));
+			if (rc)	return rc;
+			break;
+		case OV511_USOPT_CONTRAST:
+			rc = sensor_get_contrast(ov511, &(opt.val));
+			if (rc)	return rc;
+			break;
+		default:
+			err("Invalid get short option number");
+			return -EINVAL;
+		}
+
+		if (copy_to_user(arg, &opt, sizeof(opt)))
+			return -EFAULT;
+
+		return 0;
+	}
+	case OV511IOC_SUSHORT:
+	{
+		struct ov511_ushort_opt opt;
+
+		if (copy_from_user(&opt, arg, sizeof(opt)))
+			return -EFAULT;
+
+		switch (opt.optnum) {
+		case OV511_USOPT_BRIGHT:
+			rc = sensor_set_brightness(ov511, opt.val);
+			if (rc)	return rc;
+			break;
+		case OV511_USOPT_SAT:
+			rc = sensor_set_saturation(ov511, opt.val);
+			if (rc)	return rc;
+			break;
+		case OV511_USOPT_HUE:
+			rc = sensor_set_hue(ov511, opt.val);
+			if (rc)	return rc;
+			break;
+		case OV511_USOPT_CONTRAST:
+			rc = sensor_set_contrast(ov511, opt.val);
+			if (rc)	return rc;
+			break;
+		default:
+			err("Invalid set short option number");
+			return -EINVAL;
+		}
+
+		return 0;
+	}
+	case OV511IOC_GUINT:
+	{
+		struct ov511_uint_opt opt;
+
+		if (copy_from_user(&opt, arg, sizeof(opt)))
+			return -EFAULT;
+
+		switch (opt.optnum) {
+		case OV511_UIOPT_POWER_FREQ:
+			opt.val = ov511->lightfreq;
+			break;
+		case OV511_UIOPT_BFILTER:
+			opt.val = ov511->bandfilt;
+			break;
+		case OV511_UIOPT_LED:
+			opt.val = ov511->led_policy;
+			break;
+		case OV511_UIOPT_DEBUG:
+			opt.val = debug;
+			break;
+		case OV511_UIOPT_COMPRESS:
+			opt.val = ov511->compress;
+			break;
+		default:
+			err("Invalid get int option number");
+			return -EINVAL;
+		}
+
+		if (copy_to_user(arg, &opt, sizeof(opt)))
+			return -EFAULT;
+
+		return 0;
+	}
+	case OV511IOC_SUINT:
+	{
+		struct ov511_uint_opt opt;
+
+		if (copy_from_user(&opt, arg, sizeof(opt)))
+			return -EFAULT;
+
+		switch (opt.optnum) {
+		case OV511_UIOPT_POWER_FREQ:
+			rc = sensor_set_light_freq(ov511, opt.val);
+			if (rc)	return rc;
+			break;
+		case OV511_UIOPT_BFILTER:
+			rc = sensor_set_banding_filter(ov511, opt.val);
+			if (rc)	return rc;
+			break;
+		case OV511_UIOPT_LED:
+			if (opt.val <= 2) {
+				ov511->led_policy = opt.val;
+				if (ov511->led_policy == LED_OFF)
+					ov51x_led_control(ov511, 0);
+				else if (ov511->led_policy == LED_ON)
+					ov51x_led_control(ov511, 1);
+			} else {
+				return -EINVAL;
+			}
+			break;
+		case OV511_UIOPT_DEBUG:
+			if (opt.val <= 5)
+				debug = opt.val;
+			else
+				return -EINVAL;
+			break;
+		case OV511_UIOPT_COMPRESS:
+			ov511->compress = opt.val;
+			if (ov511->compress) {
+				if (ov511->bridge == BRG_OV511 ||
+				    ov511->bridge == BRG_OV511PLUS)
+					ov511_init_compression(ov511);
+				else if (ov511->bridge == BRG_OV518 ||
+					 ov511->bridge == BRG_OV518PLUS)
+					ov518_init_compression(ov511);
+			}
+			break;
+		default:
+			err("Invalid get int option number");
+			return -EINVAL;
+		}
+
+		return 0;
+	}
+	case OV511IOC_WI2C:
+	{
+		struct ov511_i2c_struct w;
+
+		if (copy_from_user(&w, arg, sizeof(w)))
+			return -EFAULT;
+
+		return ov51x_i2c_write_slave(ov511, w.slave, w.reg, w.value,
+			w.mask);
+	}
+	case OV511IOC_RI2C:
+	{
+		struct ov511_i2c_struct r;
+
+		if (copy_from_user(&r, arg, sizeof(r)))
+			return -EFAULT;
+
+		rc = ov51x_i2c_read_slave(ov511, r.slave, r.reg);
+		if (rc < 0)
+			return rc;
+
+		r.value = rc;
+
+		if (copy_to_user(arg, &r, sizeof(r)))
+			return -EFAULT;
+
+		return 0;
+	}
+	default:
+		return -EINVAL;
+	} /* end switch */
+
+	return 0;
+}
+#endif
+
 /****************************************************************************
  *
- * OV511/OV7610 configuration
+ * OV511 and sensor configuration
  *
  ***************************************************************************/
 
-static int ov76xx_configure(struct usb_ov511 *ov511)
+/* This initializes the OV7610, OV7620, or OV7620AE sensor. The OV7620AE uses
+ * the same register settings as the OV7610, since they are very similar.
+ */
+static int 
+ov7xx0_configure(struct usb_ov511 *ov511)
 {
-	struct usb_device *dev = ov511->dev;
 	int i, success;
 	int rc;
 
@@ -2832,9 +5863,11 @@
 		{ OV511_I2C_BUS, 0x23, 0x2a },
 		{ OV511_I2C_BUS, 0x24, 0x10 },
 		{ OV511_I2C_BUS, 0x25, 0x8a },
+		{ OV511_I2C_BUS, 0x26, 0xa2 },
 		{ OV511_I2C_BUS, 0x27, 0xc2 },
 		{ OV511_I2C_BUS, 0x2a, 0x04 },
 		{ OV511_I2C_BUS, 0x2c, 0xfe },
+		{ OV511_I2C_BUS, 0x2d, 0x93 },
 		{ OV511_I2C_BUS, 0x30, 0x71 },
 		{ OV511_I2C_BUS, 0x31, 0x60 },
 		{ OV511_I2C_BUS, 0x32, 0x26 },
@@ -2848,81 +5881,96 @@
 	};
 
 	static struct ov511_regvals aRegvalsNorm7620[] = {
-		{ OV511_I2C_BUS, 0x10, 0xff },
-		{ OV511_I2C_BUS, 0x16, 0x06 },
-		{ OV511_I2C_BUS, 0x28, 0x24 },
-		{ OV511_I2C_BUS, 0x2b, 0xac },
-		{ OV511_I2C_BUS, 0x12, 0x00 },
-		{ OV511_I2C_BUS, 0x28, 0x24 },
-		{ OV511_I2C_BUS, 0x0f, 0x85 },	/* lg's setting */
-		{ OV511_I2C_BUS, 0x15, 0x01 },
-		{ OV511_I2C_BUS, 0x23, 0x00 },
-		{ OV511_I2C_BUS, 0x24, 0x10 },
-		{ OV511_I2C_BUS, 0x25, 0x8a },
-		{ OV511_I2C_BUS, 0x27, 0xe2 },
-		{ OV511_I2C_BUS, 0x2a, 0x00 },
-		{ OV511_I2C_BUS, 0x2c, 0xfe },
-		{ OV511_I2C_BUS, 0x30, 0x71 },
-		{ OV511_I2C_BUS, 0x31, 0x60 },
-		{ OV511_I2C_BUS, 0x32, 0x26 },
-		{ OV511_I2C_BUS, 0x33, 0x20 },
-		{ OV511_I2C_BUS, 0x34, 0x48 },
-		{ OV511_I2C_BUS, 0x12, 0x24 },
-		{ OV511_I2C_BUS, 0x11, 0x01 },
+		{ OV511_I2C_BUS, 0x00, 0x00 },
+		{ OV511_I2C_BUS, 0x01, 0x80 },
+		{ OV511_I2C_BUS, 0x02, 0x80 },
+		{ OV511_I2C_BUS, 0x03, 0xc0 },
+		{ OV511_I2C_BUS, 0x06, 0x60 },
+		{ OV511_I2C_BUS, 0x07, 0x00 },
+		{ OV511_I2C_BUS, 0x0c, 0x24 },
 		{ OV511_I2C_BUS, 0x0c, 0x24 },
 		{ OV511_I2C_BUS, 0x0d, 0x24 },
+		{ OV511_I2C_BUS, 0x11, 0x01 },
+		{ OV511_I2C_BUS, 0x12, 0x24 },
+		{ OV511_I2C_BUS, 0x13, 0x01 },
+		{ OV511_I2C_BUS, 0x14, 0x84 },
+		{ OV511_I2C_BUS, 0x15, 0x01 },
+		{ OV511_I2C_BUS, 0x16, 0x03 },
+		{ OV511_I2C_BUS, 0x17, 0x2f },
+		{ OV511_I2C_BUS, 0x18, 0xcf },
+		{ OV511_I2C_BUS, 0x19, 0x06 },
+		{ OV511_I2C_BUS, 0x1a, 0xf5 },
+		{ OV511_I2C_BUS, 0x1b, 0x00 },
+		{ OV511_I2C_BUS, 0x20, 0x18 },
+		{ OV511_I2C_BUS, 0x21, 0x80 },
+		{ OV511_I2C_BUS, 0x22, 0x80 },
+		{ OV511_I2C_BUS, 0x23, 0x00 },
+		{ OV511_I2C_BUS, 0x26, 0xa2 },
+		{ OV511_I2C_BUS, 0x27, 0xea },
+		{ OV511_I2C_BUS, 0x28, 0x20 },
+		{ OV511_I2C_BUS, 0x29, 0x00 },
+		{ OV511_I2C_BUS, 0x2a, 0x10 },
+		{ OV511_I2C_BUS, 0x2b, 0x00 },
+		{ OV511_I2C_BUS, 0x2c, 0x88 },
+		{ OV511_I2C_BUS, 0x2d, 0x91 },
+		{ OV511_I2C_BUS, 0x2e, 0x80 },
+		{ OV511_I2C_BUS, 0x2f, 0x44 },
+		{ OV511_I2C_BUS, 0x60, 0x27 },
+		{ OV511_I2C_BUS, 0x61, 0x02 },
+		{ OV511_I2C_BUS, 0x62, 0x5f },
+		{ OV511_I2C_BUS, 0x63, 0xd5 },
+		{ OV511_I2C_BUS, 0x64, 0x57 },
+		{ OV511_I2C_BUS, 0x65, 0x83 },
+		{ OV511_I2C_BUS, 0x66, 0x55 },
+		{ OV511_I2C_BUS, 0x67, 0x92 },
+		{ OV511_I2C_BUS, 0x68, 0xcf },
+		{ OV511_I2C_BUS, 0x69, 0x76 },
+		{ OV511_I2C_BUS, 0x6a, 0x22 },
+		{ OV511_I2C_BUS, 0x6b, 0x00 },
+		{ OV511_I2C_BUS, 0x6c, 0x02 },
+		{ OV511_I2C_BUS, 0x6d, 0x44 },
+		{ OV511_I2C_BUS, 0x6e, 0x80 },
+		{ OV511_I2C_BUS, 0x6f, 0x1d },
+		{ OV511_I2C_BUS, 0x70, 0x8b },
+		{ OV511_I2C_BUS, 0x71, 0x00 },
+		{ OV511_I2C_BUS, 0x72, 0x14 },
+		{ OV511_I2C_BUS, 0x73, 0x54 },
+		{ OV511_I2C_BUS, 0x74, 0x00 },
+		{ OV511_I2C_BUS, 0x75, 0x8e },
+		{ OV511_I2C_BUS, 0x76, 0x00 },
+		{ OV511_I2C_BUS, 0x77, 0xff },
+		{ OV511_I2C_BUS, 0x78, 0x80 },
+		{ OV511_I2C_BUS, 0x79, 0x80 },
+		{ OV511_I2C_BUS, 0x7a, 0x80 },
+		{ OV511_I2C_BUS, 0x7b, 0xe2 },
+		{ OV511_I2C_BUS, 0x7c, 0x00 },
 		{ OV511_DONE_BUS, 0x0, 0x00 },
 	};
 
-	PDEBUG (4, "starting configuration");
+	PDEBUG(4, "starting configuration");
 
 	/* This looks redundant, but is necessary for WebCam 3 */
-	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE,
-	                    OV7610_I2C_WRITE_ID) < 0)
-		return -1;
-
-	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ,
-	                    OV7610_I2C_READ_ID) < 0)
-		return -1;
-
-	if (ov511_reset(dev, OV511_RESET_NOREGS) < 0)
+	ov511->primary_i2c_slave = OV7xx0_I2C_WRITE_ID;
+	if (ov51x_set_slave_ids(ov511, OV7xx0_I2C_WRITE_ID,
+				OV7xx0_I2C_READ_ID) < 0)
 		return -1;
 
-	/* Reset the 76xx */ 
-	if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1;
-
-	/* Wait for it to initialize */ 
-	schedule_timeout (1 + 150 * HZ / 1000);
-
-	for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) {
-		if ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) == 0x7F) &&
-		    (ov511_i2c_read(dev, OV7610_REG_ID_LOW) == 0xA2)) {
-			success = 1;
-			continue;
-		}
-
-		/* Reset the 76xx */ 
-		if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1;
-		/* Wait for it to initialize */ 
-		schedule_timeout (1 + 150 * HZ / 1000);
-		/* Dummy read to sync I2C */
-		if (ov511_i2c_read(dev, 0x00) < 0) return -1;
-	}
-
-	if (success) {
-		PDEBUG(1, "I2C synced in %d attempt(s) (method 1)", i);
+	if (ov51x_init_ov_sensor(ov511) >= 0) {
+		PDEBUG(1, "OV7xx0 sensor initalized (method 1)");
 	} else {
 		/* Reset the 76xx */
-		if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1;
+		if (ov51x_i2c_write(ov511, 0x12, 0x80) < 0) return -1;
 
 		/* Wait for it to initialize */
-		schedule_timeout (1 + 150 * HZ / 1000);
+		schedule_timeout(1 + 150 * HZ / 1000);
 
 		i = 0;
 		success = 0;
 		while (i <= i2c_detect_tries) {
-			if ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) == 0x7F) &&
-			    (ov511_i2c_read(dev, OV7610_REG_ID_LOW) == 0xA2)) {
+			if ((ov51x_i2c_read(ov511,
+					    OV7610_REG_ID_HIGH) == 0x7F) &&
+			    (ov51x_i2c_read(ov511,
+					    OV7610_REG_ID_LOW) == 0xA2)) {
 				success = 1;
 				break;
 			} else {
@@ -2930,311 +5978,705 @@
 			}
 		}
 
-		if ((i == i2c_detect_tries) && (success == 0)) {
-			err("Failed to read sensor ID. You might not have an OV7610/20,");
-			err("or it may be not responding. Report this to");
-			err("mwm@i.am");
-			return -1;
+// Was (i == i2c_detect_tries) previously. This obviously used to always report
+// success. Whether anyone actually depended on that bug is unknown
+		if ((i >= i2c_detect_tries) && (success == 0)) {
+			err("Failed to read sensor ID. You might not have an");
+			err("OV7610/20, or it may be not responding. Report");
+			err("this to " EMAIL);
+			err("This is only a warning. You can attempt to use");
+			err("your camera anyway");
+// Only issue a warning for now  
+//			return -1;
 		} else {
-			PDEBUG(1, "I2C synced in %d attempt(s) (method 2)", i+1);
+			PDEBUG(1, "OV7xx0 initialized (method 2, %dx)", i+1);
 		}
 	}
 
-	/* Detect sensor if user didn't use override param */
-	if (sensor == 0) {
-		rc = ov511_i2c_read(dev, OV7610_REG_COM_I);
+	/* Detect sensor (sub)type */
+	rc = ov51x_i2c_read(ov511, OV7610_REG_COM_I);
 
-		if (rc < 0) {
-			err("Error detecting sensor type");
-			return -1;
-		} else if((rc & 3) == 3) {
-			info("Sensor is an OV7610");
-			ov511->sensor = SEN_OV7610;
-		} else if((rc & 3) == 1) {
+	if (rc < 0) {
+		err("Error detecting sensor type");
+		return -1;
+	} else if ((rc & 3) == 3) {
+		info("Sensor is an OV7610");
+		ov511->sensor = SEN_OV7610;
+	} else if ((rc & 3) == 1) {
+		/* I don't know what's different about the 76BE yet */
+		if (ov51x_i2c_read(ov511, 0x15) & 1)
 			info("Sensor is an OV7620AE");
-			ov511->sensor = SEN_OV7620AE;
-		} else if((rc & 3) == 0) {
-			info("Sensor is an OV7620");
+		else
+			info("Sensor is an OV76BE");
+
+		/* OV511+ will return all zero isoc data unless we
+		 * configure the sensor as a 7620. Someone needs to
+		 * find the exact reg. setting that causes this. */
+		if (ov511->bridge == BRG_OV511PLUS) {
+			info("Enabling 511+/7620AE workaround");
 			ov511->sensor = SEN_OV7620;
 		} else {
-			err("Unknown image sensor version: %d", rc & 3);
+			ov511->sensor = SEN_OV7620AE;
+		}
+	} else if ((rc & 3) == 0) {
+		info("Sensor is an OV7620");
+		ov511->sensor = SEN_OV7620;
+	} else {
+		err("Unknown image sensor version: %d", rc & 3);
+		return -1;
+	}
+
+	if (ov511->sensor == SEN_OV7620) {
+		PDEBUG(4, "Writing 7620 registers");
+		if (ov511_write_regvals(ov511, aRegvalsNorm7620))
+			return -1;
+	} else {
+		PDEBUG(4, "Writing 7610 registers");
+		if (ov511_write_regvals(ov511, aRegvalsNorm7610))
+			return -1;
+	}
+
+	/* Set sensor-specific vars */
+	ov511->maxwidth = 640;
+	ov511->maxheight = 480;
+	ov511->minwidth = 64;
+	ov511->minheight = 48;
+
+	// FIXME: These do not match the actual settings yet
+	ov511->brightness = 0x80 << 8;
+	ov511->contrast = 0x80 << 8;
+	ov511->colour = 0x80 << 8;
+	ov511->hue = 0x80 << 8;
+
+	return 0;
+}
+
+/* This initializes the OV6620, OV6630, OV6630AE, or OV6630AF sensor. */
+static int 
+ov6xx0_configure(struct usb_ov511 *ov511)
+{
+	int rc;
+
+	static struct ov511_regvals aRegvalsNorm6x20[] = {
+		{ OV511_I2C_BUS, 0x12, 0x80 }, /* reset */
+		{ OV511_I2C_BUS, 0x11, 0x01 },
+		{ OV511_I2C_BUS, 0x03, 0x60 },
+		{ OV511_I2C_BUS, 0x05, 0x7f }, /* For when autoadjust is off */
+		{ OV511_I2C_BUS, 0x07, 0xa8 },
+		/* The ratio of 0x0c and 0x0d  controls the white point */
+		{ OV511_I2C_BUS, 0x0c, 0x24 },
+		{ OV511_I2C_BUS, 0x0d, 0x24 },
+		{ OV511_I2C_BUS, 0x12, 0x24 }, /* Enable AGC */
+		{ OV511_I2C_BUS, 0x14, 0x04 },
+		/* 0x16: 0x06 helps frame stability with moving objects */
+		{ OV511_I2C_BUS, 0x16, 0x06 },
+//		{ OV511_I2C_BUS, 0x20, 0x30 }, /* Aperture correction enable */
+		{ OV511_I2C_BUS, 0x26, 0xb2 }, /* BLC enable */
+		/* 0x28: 0x05 Selects RGB format if RGB on */
+		{ OV511_I2C_BUS, 0x28, 0x05 },
+		{ OV511_I2C_BUS, 0x2a, 0x04 }, /* Disable framerate adjust */
+//		{ OV511_I2C_BUS, 0x2b, 0xac }, /* Framerate; Set 2a[7] first */
+		{ OV511_I2C_BUS, 0x2d, 0x99 },
+		{ OV511_I2C_BUS, 0x34, 0xd2 }, /* Max A/D range */
+		{ OV511_I2C_BUS, 0x38, 0x8b },
+		{ OV511_I2C_BUS, 0x39, 0x40 },
+		
+		{ OV511_I2C_BUS, 0x3c, 0x39 }, /* Enable AEC mode changing */
+		{ OV511_I2C_BUS, 0x3c, 0x3c }, /* Change AEC mode */
+		{ OV511_I2C_BUS, 0x3c, 0x24 }, /* Disable AEC mode changing */
+
+		{ OV511_I2C_BUS, 0x3d, 0x80 },
+		/* These next two registers (0x4a, 0x4b) are undocumented. They
+		 * control the color balance */
+		{ OV511_I2C_BUS, 0x4a, 0x80 },
+		{ OV511_I2C_BUS, 0x4b, 0x80 },
+		{ OV511_I2C_BUS, 0x4d, 0xd2 }, /* This reduces noise a bit */
+		{ OV511_I2C_BUS, 0x4e, 0xc1 },
+		{ OV511_I2C_BUS, 0x4f, 0x04 },
+// Do 50-53 have any effect?
+// Toggle 0x12[2] off and on here?
+		{ OV511_DONE_BUS, 0x0, 0x00 },
+	};
+
+	/* This chip is undocumented so many of these are guesses. OK=verified,
+	 * A=Added since 6620, U=unknown function (not a 6620 reg) */
+	static struct ov511_regvals aRegvalsNorm6x30[] = {
+	/*OK*/	{ OV511_I2C_BUS, 0x12, 0x80 }, /* reset */
+	/*00?*/	{ OV511_I2C_BUS, 0x11, 0x01 },
+	/*OK*/	{ OV511_I2C_BUS, 0x03, 0x60 },
+	/*0A?*/	{ OV511_I2C_BUS, 0x05, 0x7f }, /* For when autoadjust is off */
+		{ OV511_I2C_BUS, 0x07, 0xa8 },
+		/* The ratio of 0x0c and 0x0d  controls the white point */
+	/*OK*/	{ OV511_I2C_BUS, 0x0c, 0x24 },
+	/*OK*/	{ OV511_I2C_BUS, 0x0d, 0x24 },
+	/*A*/	{ OV511_I2C_BUS, 0x0e, 0x20 },
+
+//	/*24?*/	{ OV511_I2C_BUS, 0x12, 0x28 }, /* Enable AGC */
+//		{ OV511_I2C_BUS, 0x12, 0x24 }, /* Enable AGC */
+
+//	/*A*/	{ OV511_I2C_BUS, 0x13, 0x21 },
+//	/*A*/	{ OV511_I2C_BUS, 0x13, 0x25 }, /* Tristate Y and UV busses */
+
+//	/*04?*/	{ OV511_I2C_BUS, 0x14, 0x80 },
+		/* 0x16: 0x06 helps frame stability with moving objects */
+	/*03?*/	{ OV511_I2C_BUS, 0x16, 0x06 },
+//	/*OK*/	{ OV511_I2C_BUS, 0x20, 0x30 }, /* Aperture correction enable */
+		// 21 & 22? The suggested values look wrong. Go with default
+	/*A*/	{ OV511_I2C_BUS, 0x23, 0xc0 },
+	/*A*/	{ OV511_I2C_BUS, 0x25, 0x9a }, // Check this against default
+//	/*OK*/	{ OV511_I2C_BUS, 0x26, 0xb2 }, /* BLC enable */
+
+		/* 0x28: 0x05 Selects RGB format if RGB on */
+//	/*04?*/	{ OV511_I2C_BUS, 0x28, 0x05 },
+//	/*04?*/	{ OV511_I2C_BUS, 0x28, 0x45 }, // DEBUG: Tristate UV bus
+
+	/*OK*/	{ OV511_I2C_BUS, 0x2a, 0x04 }, /* Disable framerate adjust */
+//	/*OK*/	{ OV511_I2C_BUS, 0x2b, 0xac }, /* Framerate; Set 2a[7] first */
+//	/*U*/	{ OV511_I2C_BUS, 0x2c, 0xa0 },
+		{ OV511_I2C_BUS, 0x2d, 0x99 },
+//	/*A*/	{ OV511_I2C_BUS, 0x33, 0x26 }, // Reserved bits on 6620
+//	/*d2?*/	{ OV511_I2C_BUS, 0x34, 0x03 }, /* Max A/D range */
+//	/*U*/	{ OV511_I2C_BUS, 0x36, 0x8f }, // May not be necessary
+//	/*U*/	{ OV511_I2C_BUS, 0x37, 0x80 }, // May not be necessary
+//	/*8b?*/	{ OV511_I2C_BUS, 0x38, 0x83 },
+//	/*40?*/	{ OV511_I2C_BUS, 0x39, 0xc0 }, // 6630 adds bit 7
+//		{ OV511_I2C_BUS, 0x3c, 0x39 }, /* Enable AEC mode changing */
+//		{ OV511_I2C_BUS, 0x3c, 0x3c }, /* Change AEC mode */
+//		{ OV511_I2C_BUS, 0x3c, 0x24 }, /* Disable AEC mode changing */
+	/*OK*/	{ OV511_I2C_BUS, 0x3d, 0x80 },
+//	/*A*/	{ OV511_I2C_BUS, 0x3f, 0x0e },
+//	/*U*/	{ OV511_I2C_BUS, 0x40, 0x00 },
+//	/*U*/	{ OV511_I2C_BUS, 0x41, 0x00 },
+//	/*U*/	{ OV511_I2C_BUS, 0x42, 0x80 },
+//	/*U*/	{ OV511_I2C_BUS, 0x43, 0x3f },
+//	/*U*/	{ OV511_I2C_BUS, 0x44, 0x80 },
+//	/*U*/	{ OV511_I2C_BUS, 0x45, 0x20 },
+//	/*U*/	{ OV511_I2C_BUS, 0x46, 0x20 },
+//	/*U*/	{ OV511_I2C_BUS, 0x47, 0x80 },
+//	/*U*/	{ OV511_I2C_BUS, 0x48, 0x7f },
+//	/*U*/	{ OV511_I2C_BUS, 0x49, 0x00 },
+
+		/* These next two registers (0x4a, 0x4b) are undocumented. They
+		 * control the color balance */
+//	/*OK?*/	{ OV511_I2C_BUS, 0x4a, 0x80 }, // Check these
+//	/*OK?*/	{ OV511_I2C_BUS, 0x4b, 0x80 },
+//	/*U*/	{ OV511_I2C_BUS, 0x4c, 0xd0 }, 
+	/*d2?*/	{ OV511_I2C_BUS, 0x4d, 0x10 }, /* This reduces noise a bit */
+	/*c1?*/	{ OV511_I2C_BUS, 0x4e, 0x40 },
+	/*04?*/	{ OV511_I2C_BUS, 0x4f, 0x07 },
+//	/*U*/	{ OV511_I2C_BUS, 0x50, 0xff },
+	/*U*/	{ OV511_I2C_BUS, 0x54, 0x23 },
+//	/*U*/	{ OV511_I2C_BUS, 0x55, 0xff },
+//	/*U*/	{ OV511_I2C_BUS, 0x56, 0x12 },
+	/*U*/	{ OV511_I2C_BUS, 0x57, 0x81 },
+//	/*U*/	{ OV511_I2C_BUS, 0x58, 0x75 },
+	/*U*/	{ OV511_I2C_BUS, 0x59, 0x01 },
+	/*U*/	{ OV511_I2C_BUS, 0x5a, 0x2c },
+	/*U*/	{ OV511_I2C_BUS, 0x5b, 0x0f },
+//	/*U*/	{ OV511_I2C_BUS, 0x5c, 0x10 },
+		{ OV511_DONE_BUS, 0x0, 0x00 },
+	};
+
+	PDEBUG(4, "starting sensor configuration");
+	
+	if (ov51x_init_ov_sensor(ov511) < 0) {
+		err("Failed to read sensor ID. You might not have an OV6xx0,");
+		err("or it may be not responding. Report this to " EMAIL);
+		return -1;
+	} else {
+		PDEBUG(1, "OV6xx0 sensor detected");
+	}
+
+	/* Detect sensor (sub)type */
+	rc = ov51x_i2c_read(ov511, OV7610_REG_COM_I);
+
+	if (rc < 0) {
+		err("Error detecting sensor type");
+		return -1;
+	} else if ((rc & 3) == 0) {
+		info("Sensor is an OV6630");
+		ov511->sensor = SEN_OV6630;
+	} else if ((rc & 3) == 1) {
+		info("Sensor is an OV6620");
+		ov511->sensor = SEN_OV6620;
+	} else if ((rc & 3) == 2) {
+		info("Sensor is an OV6630AE");
+		ov511->sensor = SEN_OV6630;
+	} else if ((rc & 3) == 3) {
+		info("Sensor is an OV6630AF");
+		ov511->sensor = SEN_OV6630;
+	} 
+
+	/* Set sensor-specific vars */
+	if (ov511->sensor == SEN_OV6620) {
+		ov511->maxwidth = 352;
+		ov511->maxheight = 288;
+	} else {
+		/* 352x288 not working with OV518 yet */
+		ov511->maxwidth = 320;
+		ov511->maxheight = 240;
+	}
+	ov511->minwidth = 64;
+	ov511->minheight = 48;
+
+	// FIXME: These do not match the actual settings yet
+	ov511->brightness = 0x80 << 8;
+	ov511->contrast = 0x80 << 8;
+	ov511->colour = 0x80 << 8;
+	ov511->hue = 0x80 << 8;
+
+	if (ov511->sensor == SEN_OV6620) {
+		PDEBUG(4, "Writing 6x20 registers");
+		if (ov511_write_regvals(ov511, aRegvalsNorm6x20))
+			return -1;
+	} else {
+		PDEBUG(4, "Writing 6x30 registers");
+		if (ov511_write_regvals(ov511, aRegvalsNorm6x30))
+			return -1;
+	}
+	
+	return 0;
+}
+
+/* This initializes the KS0127 and KS0127B video decoders. */
+static int 
+ks0127_configure(struct usb_ov511 *ov511)
+{
+	int rc;
+
+// FIXME: I don't know how to sync or reset it yet
+#if 0
+	if (ov51x_init_ks_sensor(ov511) < 0) {
+		err("Failed to initialize the KS0127");
+		return -1;
+	} else {
+		PDEBUG(1, "KS012x(B) sensor detected");
+	}
+#endif
+
+	/* Detect decoder subtype */
+	rc = ov51x_i2c_read(ov511, 0x00);
+	if (rc < 0) {
+		err("Error detecting sensor type");
+		return -1;
+	} else if (rc & 0x08) {
+		rc = ov51x_i2c_read(ov511, 0x3d);
+		if (rc < 0) {
+			err("Error detecting sensor type");
 			return -1;
+		} else if ((rc & 0x0f) == 0) {
+			info("Sensor is a KS0127");
+			ov511->sensor = SEN_KS0127;
+		} else if ((rc & 0x0f) == 9) {
+			info("Sensor is a KS0127B Rev. A");
+			ov511->sensor = SEN_KS0127B;
 		}
-	} else {	/* sensor != 0; user overrode detection */
-		ov511->sensor = sensor;
-		info("Sensor set to type %d", ov511->sensor);
+	} else {
+		err("Error: Sensor is an unsupported KS0122");
+		return -1;
 	}
 
-	if (ov511->sensor == SEN_OV7620) {
-		PDEBUG(4, "Writing 7620 registers");
-		if (ov511_write_regvals(dev, aRegvalsNorm7620))
-			return -1;
+	/* Set sensor-specific vars */
+	ov511->maxwidth = 640;
+	ov511->maxheight = 480;
+	ov511->minwidth = 64;
+	ov511->minheight = 48;
+
+	// FIXME: These do not match the actual settings yet
+	ov511->brightness = 0x80 << 8;
+	ov511->contrast = 0x80 << 8;
+	ov511->colour = 0x80 << 8;
+	ov511->hue = 0x80 << 8;
+
+	/* This device is not supported yet. Bail out now... */
+	err("This sensor is not supported yet.");
+	return -1;
+
+	return 0;
+}
+
+/* This initializes the SAA7111A video decoder. */
+static int 
+saa7111a_configure(struct usb_ov511 *ov511)
+{
+	struct usb_device *dev = ov511->dev;
+	int rc;
+
+	/* Since there is no register reset command, all registers must be
+	 * written, otherwise gives erratic results */
+	static struct ov511_regvals aRegvalsNormSAA7111A[] = {
+		{ OV511_I2C_BUS, 0x06, 0xce },
+		{ OV511_I2C_BUS, 0x07, 0x00 },
+		{ OV511_I2C_BUS, 0x10, 0x44 }, /* YUV422, 240/286 lines */
+		{ OV511_I2C_BUS, 0x0e, 0x01 }, /* NTSC M or PAL BGHI */
+		{ OV511_I2C_BUS, 0x00, 0x00 },
+		{ OV511_I2C_BUS, 0x01, 0x00 },
+		{ OV511_I2C_BUS, 0x03, 0x23 },
+		{ OV511_I2C_BUS, 0x04, 0x00 },
+		{ OV511_I2C_BUS, 0x05, 0x00 },
+		{ OV511_I2C_BUS, 0x08, 0xc8 }, /* Auto field freq */
+		{ OV511_I2C_BUS, 0x09, 0x01 }, /* Chrom. trap off, APER=0.25 */
+		{ OV511_I2C_BUS, 0x0a, 0x80 }, /* BRIG=128 */
+		{ OV511_I2C_BUS, 0x0b, 0x40 }, /* CONT=1.0 */
+		{ OV511_I2C_BUS, 0x0c, 0x40 }, /* SATN=1.0 */
+		{ OV511_I2C_BUS, 0x0d, 0x00 }, /* HUE=0 */
+		{ OV511_I2C_BUS, 0x0f, 0x00 },
+		{ OV511_I2C_BUS, 0x11, 0x0c },
+		{ OV511_I2C_BUS, 0x12, 0x00 },
+		{ OV511_I2C_BUS, 0x13, 0x00 },
+		{ OV511_I2C_BUS, 0x14, 0x00 },
+		{ OV511_I2C_BUS, 0x15, 0x00 },
+		{ OV511_I2C_BUS, 0x16, 0x00 },
+		{ OV511_I2C_BUS, 0x17, 0x00 },
+		{ OV511_I2C_BUS, 0x02, 0xc0 },	/* Composite input 0 */
+		{ OV511_DONE_BUS, 0x0, 0x00 },
+	};
+
+// FIXME: I don't know how to sync or reset it yet
+#if 0
+	if (ov51x_init_saa_sensor(ov511) < 0) {
+		err("Failed to initialize the SAA7111A");
+		return -1;
 	} else {
-		PDEBUG(4, "Writing 7610 registers");
-		if (ov511_write_regvals(dev, aRegvalsNorm7610))
-			return -1;
+		PDEBUG(1, "SAA7111A sensor detected");
 	}
+#endif
 
 	/* Set sensor-specific vars */
 	ov511->maxwidth = 640;
-	ov511->maxheight = 480;
+	ov511->maxheight = 480;		/* Even/Odd fields */
+	ov511->minwidth = 320;
+	ov511->minheight = 240;		/* Even field only */
+
+	ov511->has_decoder = 1;
+	ov511->num_inputs = 8;
+	ov511->norm = VIDEO_MODE_AUTO;
+	ov511->stop_during_set = 0;	/* Decoder guarantees stable image */
+
+	/* Decoder doesn't change these values, so we use these instead of
+	 * acutally reading the registers (which doesn't work) */
+	ov511->brightness = 0x80 << 8;
+	ov511->contrast = 0x40 << 9;
+	ov511->colour = 0x40 << 9;
+	ov511->hue = 32768;
 
-	if (aperture < 0) {          /* go with the default */
-		if (ov511_i2c_write(dev, 0x26, 0xa2) < 0) return -1;
-	} else if (aperture <= 0xf) {  /* user overrode default */
-		if (ov511_i2c_write(dev, 0x26, (aperture << 4) + 2) < 0)
-			return -1;
-	} else {
-		err("Invalid setting for aperture; legal value: 0 - 15");
+	PDEBUG(4, "Writing SAA7111A registers");
+	if (ov511_write_regvals(ov511, aRegvalsNormSAA7111A))
 		return -1;
-	}
 
-	if (autoadjust) {
-		if (ov511_i2c_write(dev, 0x13, 0x01) < 0) return -1;
-		if (ov511_i2c_write(dev, 0x2d, 
-		     ov511->sensor==SEN_OV7620?0x91:0x93) < 0) return -1;
+	/* Detect version of decoder. This must be done after writing the
+         * initial regs or the decoder will lock up. */
+	rc = ov51x_i2c_read(ov511, 0x00);
+
+	if (rc < 0) {
+		err("Error detecting sensor version");
+		return -1;
 	} else {
-		if (ov511_i2c_write(dev, 0x13, 0x00) < 0) return -1;
-		if (ov511_i2c_write(dev, 0x2d, 
-		     ov511->sensor==SEN_OV7620?0x81:0x83) < 0) return -1;
-		ov511_i2c_write(dev, 0x28, ov511_i2c_read(dev, 0x28) | 8);
+		info("Sensor is an SAA7111A (version 0x%x)", rc);
+		ov511->sensor = SEN_SAA7111A;
 	}
 
+	// FIXME: Fix this for OV518(+)
+	/* Latch to negative edge of clock. Otherwise, we get incorrect
+	 * colors and jitter in the digital signal. */
+	if (ov511->bridge == BRG_OV511 || ov511->bridge == BRG_OV511PLUS)
+		ov511_reg_write(dev, 0x11, 0x00);
+	else
+		warn("SAA7111A not yet supported with OV518/OV518+");
+
 	return 0;
 }
 
-static int ov6xx0_configure(struct usb_ov511 *ov511)
+/* This initializes the OV511/OV511+ and the sensor */
+static int 
+ov511_configure(struct usb_ov511 *ov511)
 {
 	struct usb_device *dev = ov511->dev;
-	int i, success, rc;
+	int i;
 
-	static struct ov511_regvals aRegvalsNorm6x20[] = {
-		{ OV511_I2C_BUS, 0x12, 0x80 },  /* reset */
-		{ OV511_I2C_BUS, 0x11, 0x01 },
-		{ OV511_I2C_BUS, 0x03, 0xd0 },
-		{ OV511_I2C_BUS, 0x05, 0x7f },
-		{ OV511_I2C_BUS, 0x07, 0xa8 },
-		{ OV511_I2C_BUS, 0x0c, 0x24 },
-		{ OV511_I2C_BUS, 0x0d, 0x24 },
-		{ OV511_I2C_BUS, 0x10, 0xff },  /* ? */
-		{ OV511_I2C_BUS, 0x14, 0x04 },
-		{ OV511_I2C_BUS, 0x16, 0x06 },  /* ? */
-		{ OV511_I2C_BUS, 0x19, 0x04 },
-		{ OV511_I2C_BUS, 0x1a, 0x93 },
-		{ OV511_I2C_BUS, 0x20, 0x28 },
-		{ OV511_I2C_BUS, 0x27, 0xa2 },
-		{ OV511_I2C_BUS, 0x28, 0x24 },
-		{ OV511_I2C_BUS, 0x2a, 0x04 },  /* 84? */
-		{ OV511_I2C_BUS, 0x2b, 0xac },  /* a8? */
-		{ OV511_I2C_BUS, 0x2d, 0x95 },
-		{ OV511_I2C_BUS, 0x33, 0x28 },
-		{ OV511_I2C_BUS, 0x34, 0xc7 },
-		{ OV511_I2C_BUS, 0x38, 0x8b },
-		{ OV511_I2C_BUS, 0x3c, 0x5c },
-		{ OV511_I2C_BUS, 0x3d, 0x80 },
-		{ OV511_I2C_BUS, 0x3f, 0x00 },
-		{ OV511_I2C_BUS, 0x4a, 0x80 }, /* undocumented */
-		{ OV511_I2C_BUS, 0x4b, 0x80 }, /* undocumented */
-		{ OV511_I2C_BUS, 0x4d, 0xd2 },
-		{ OV511_I2C_BUS, 0x4e, 0xc1 },
-		{ OV511_I2C_BUS, 0x4f, 0x04 },
-		{ OV511_DONE_BUS, 0x0, 0x00 },
+	static struct ov511_regvals aRegvalsInit511[] = {
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7f },
+	 	{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 },
+	 	{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7f },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3f },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3d },
+		{ OV511_DONE_BUS, 0x0, 0x00},
 	};
 
-	PDEBUG (4, "starting sensor configuration");
-	
-	/* Reset the 6xx0 */ 
-	if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1;
+	static struct ov511_regvals aRegvalsNorm511[] = {
+		{ OV511_REG_BUS, OV511_REG_DRAM_ENABLE_FLOW_CONTROL, 0x01 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x01 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x03 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x01 },
+		{ OV511_REG_BUS, OV511_REG_FIFO_BITMASK, 0x1f },
+		{ OV511_REG_BUS, OV511_OMNICE_ENABLE, 0x00 },
+		{ OV511_REG_BUS, OV511_OMNICE_LUT_ENABLE, 0x03 },
+		{ OV511_DONE_BUS, 0x0, 0x00 },
+	};
 
-	/* Wait for it to initialize */ 
-	schedule_timeout (1 + 150 * HZ / 1000);
+	static struct ov511_regvals aRegvalsNorm511Plus[] = {
+		{ OV511_REG_BUS, OV511_REG_DRAM_ENABLE_FLOW_CONTROL, 0xff },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x01 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x03 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x01 },
+		{ OV511_REG_BUS, OV511_REG_FIFO_BITMASK, 0xff },
+		{ OV511_REG_BUS, OV511_OMNICE_ENABLE, 0x00 },
+		{ OV511_REG_BUS, OV511_OMNICE_LUT_ENABLE, 0x03 },
+		{ OV511_DONE_BUS, 0x0, 0x00 },
+	};
 
-	for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) {
-		if ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) == 0x7F) &&
-		    (ov511_i2c_read(dev, OV7610_REG_ID_LOW) == 0xA2)) {
-			success = 1;
-			continue;
-		}
+	PDEBUG(4, "");
 
-		/* Reset the 6xx0 */ 
-		if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1;
-		/* Wait for it to initialize */ 
-		schedule_timeout (1 + 150 * HZ / 1000);
-		/* Dummy read to sync I2C */
-		if (ov511_i2c_read(dev, 0x00) < 0) return -1;
+	ov511->customid = ov511_reg_read(dev, OV511_REG_SYSTEM_CUSTOM_ID);
+	if (ov511->customid < 0) {
+		err("Unable to read camera bridge registers");
+		goto error;
 	}
 
-	if (success) {
-		PDEBUG(1, "I2C synced in %d attempt(s)", i);
-	} else {
-		err("Failed to read sensor ID. You might not have an OV6xx0,");
-		err("or it may be not responding. Report this to");
-		err("mwm@i.am");
-		return -1;
+	ov511->desc = -1;
+	PDEBUG (1, "CustomID = %d", ov511->customid);
+	for (i = 0; clist[i].id >= 0; i++) {
+		if (ov511->customid == clist[i].id) {
+			info("model: %s", clist[i].description);
+			ov511->desc = i;
+			break;
+		}
 	}
 
-	/* Detect sensor if user didn't use override param */
-	if (sensor == 0) {
-		rc = ov511_i2c_read(dev, OV7610_REG_COM_I);
+	if (clist[i].id == -1) {
+		err("Camera type (%d) not recognized", ov511->customid);
+		err("Please notify " EMAIL " of the name,");
+		err("manufacturer, model, and this number of your camera.");
+		err("Also include the output of the detection process.");
+	} 
 
-		if (rc < 0) {
-			err("Error detecting sensor type");
-			return -1;
-		} else {
-			info("Sensor is an OV6xx0 (version %d)", rc & 3);
-			ov511->sensor = SEN_OV6620;
-		}
-	} else {	/* sensor != 0; user overrode detection */
-		ov511->sensor = sensor;
-		info("Sensor set to type %d", ov511->sensor);
+	if (clist[i].id == 6) {	/* USB Life TV (NTSC) */
+		ov511->tuner_type = 8;		/* Temic 4036FY5 3X 1981 */
 	}
 
-	/* Set sensor-specific vars */
-	ov511->maxwidth = 352;
-	ov511->maxheight = 288;
+	if (ov511_write_regvals(ov511, aRegvalsInit511)) goto error;
 
-	PDEBUG(4, "Writing 6x20 registers");
-	if (ov511_write_regvals(dev, aRegvalsNorm6x20))
-		return -1;
+	if (ov511->led_policy == LED_OFF || ov511->led_policy == LED_AUTO)
+		ov51x_led_control(ov511, 0);
 
-	if (aperture < 0) {          /* go with the default */
-		if (ov511_i2c_write(dev, 0x26, 0xa2) < 0) return -1;
-	} else if (aperture <= 0xf) {  /* user overrode default */
-		if (ov511_i2c_write(dev, 0x26, (aperture << 4) + 2) < 0)
-			return -1;
+	/* The OV511+ has undocumented bits in the flow control register.
+	 * Setting it to 0xff fixes the corruption with moving objects. */
+	if (ov511->bridge == BRG_OV511) {
+		if (ov511_write_regvals(ov511, aRegvalsNorm511)) goto error;
+	} else if (ov511->bridge == BRG_OV511PLUS) {
+		if (ov511_write_regvals(ov511, aRegvalsNorm511Plus)) goto error;
 	} else {
-		err("Invalid setting for aperture; legal value: 0 - 15");
-		return -1;
+		err("Invalid bridge");
 	}
 
-	if (autoadjust) {
-		if (ov511_i2c_write(dev, 0x13, 0x01) < 0) return -1;
-		if (ov511_i2c_write(dev, 0x2d, 
-		     ov511->sensor==SEN_OV7620?0x91:0x93) < 0) return -1;
+	if (ov511_init_compression(ov511)) goto error;
+
+	ov511_set_packet_size(ov511, 0);
+
+	ov511->snap_enabled = snapshot;	
+
+	/* Test for 7xx0 */
+	PDEBUG(3, "Testing for 0V7xx0");
+	ov511->primary_i2c_slave = OV7xx0_I2C_WRITE_ID;
+	if (ov51x_set_slave_ids(ov511, OV7xx0_I2C_WRITE_ID,
+				OV7xx0_I2C_READ_ID) < 0)
+		goto error;
+
+	if (ov51x_i2c_write(ov511, 0x12, 0x80) < 0) {
+		/* Test for 6xx0 */
+		PDEBUG(3, "Testing for 0V6xx0");
+		ov511->primary_i2c_slave = OV6xx0_I2C_WRITE_ID;
+		if (ov51x_set_slave_ids(ov511, OV6xx0_I2C_WRITE_ID,
+					OV6xx0_I2C_READ_ID) < 0)
+			goto error;
+
+		if (ov51x_i2c_write(ov511, 0x12, 0x80) < 0) {
+			/* Test for 8xx0 */
+			PDEBUG(3, "Testing for 0V8xx0");
+			ov511->primary_i2c_slave = OV8xx0_I2C_WRITE_ID;
+			if (ov51x_set_slave_ids(ov511, OV8xx0_I2C_WRITE_ID,
+						OV8xx0_I2C_READ_ID))
+				goto error;
+
+			if (ov51x_i2c_write(ov511, 0x12, 0x80) < 0) {
+				/* Test for SAA7111A */
+				PDEBUG(3, "Testing for SAA7111A");
+				ov511->primary_i2c_slave = SAA7111A_I2C_WRITE_ID;
+				if (ov51x_set_slave_ids(ov511, SAA7111A_I2C_WRITE_ID,
+							SAA7111A_I2C_READ_ID))
+					goto error;
+
+				if (ov51x_i2c_write(ov511, 0x0d, 0x00) < 0) {
+					/* Test for KS0127 */
+					PDEBUG(3, "Testing for KS0127");
+					ov511->primary_i2c_slave = KS0127_I2C_WRITE_ID;
+					if (ov51x_set_slave_ids(ov511, KS0127_I2C_WRITE_ID,
+								KS0127_I2C_READ_ID))
+						goto error;
+
+					if (ov51x_i2c_write(ov511, 0x10, 0x00) < 0) {
+						err("Can't determine sensor slave IDs");
+		 				goto error;
+					} else {
+						if(ks0127_configure(ov511) < 0) {
+							err("Failed to configure KS0127");
+	 						goto error;
+						}
+					}
+				} else {
+					if(saa7111a_configure(ov511) < 0) {
+						err("Failed to configure SAA7111A");
+	 					goto error;
+					}
+				}
+			} else {
+				err("Detected unsupported OV8xx0 sensor");
+				goto error;
+			}
+		} else {
+			if(ov6xx0_configure(ov511) < 0) {
+				err("Failed to configure OV6xx0");
+ 				goto error;
+			}
+		}
 	} else {
-		if (ov511_i2c_write(dev, 0x13, 0x00) < 0) return -1;
-		if (ov511_i2c_write(dev, 0x2d, 
-		     ov511->sensor==SEN_OV7620?0x81:0x83) < 0) return -1;
-		ov511_i2c_write(dev, 0x28, ov511_i2c_read(dev, 0x28) | 8);
+		if(ov7xx0_configure(ov511) < 0) {
+			err("Failed to configure OV7xx0");
+	 		goto error;
+		}
 	}
 
 	return 0;
-}
 
+error:
+	err("OV511 Config failed");
+
+	return -EBUSY;
+}
 
-static int ov511_configure(struct usb_ov511 *ov511)
+/* This initializes the OV518/OV518+ and the sensor */
+static int 
+ov518_configure(struct usb_ov511 *ov511)
 {
 	struct usb_device *dev = ov511->dev;
-	int i;
 
-	static struct ov511_regvals aRegvalsInit[] = {
-		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7f },
-	 	{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 },
-	 	{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7f },
-		{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 },
-		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3f },
-		{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 },
-		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3d },
+	static struct ov511_regvals aRegvalsInit518[] = {
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x40 },
+	 	{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0xe1 },
+	 	{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3e },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0xe1 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x00 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0xe1 },
+		{ OV511_REG_BUS, 0x46, 0x00 }, 
+		{ OV511_REG_BUS, 0x5d, 0x03 },
 		{ OV511_DONE_BUS, 0x0, 0x00},
 	};
 
-	static struct ov511_regvals aRegvalsNorm511[] = {
-		{ OV511_REG_BUS, OV511_REG_DRAM_ENABLE_FLOW_CONTROL, 0x01 },
-		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x02 },
-		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x00 },
-		{ OV511_REG_BUS, OV511_REG_FIFO_BITMASK, 0x1f },
-		{ OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_Y, 0x08 },
-		{ OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_UV, 0x01 },
-		{ OV511_REG_BUS, OV511_OMNICE_PREDICTION_VERT_Y, 0x08 },
-		{ OV511_REG_BUS, OV511_OMNICE_PREDICTION_VERT_UV, 0x01 },
-		{ OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_HORIZ_Y, 0x01 },
-		{ OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_HORIZ_UV, 0x01 },
-		{ OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_VERT_Y, 0x01 },
-		{ OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_VERT_UV, 0x01 },
-		{ OV511_REG_BUS, OV511_OMNICE_ENABLE, 0x06 },
-		{ OV511_REG_BUS, OV511_OMNICE_LUT_ENABLE, 0x03 },
+	/* New values, based on Windows driver. Since what they do is not
+	 * known yet, this may be incorrect. */
+	static struct ov511_regvals aRegvalsNorm518[] = {
+		{ OV511_REG_BUS, 0x52, 0x02 }, /* Reset snapshot */
+		{ OV511_REG_BUS, 0x52, 0x01 }, /* Enable snapshot */
+		{ OV511_REG_BUS, 0x31, 0x0f },
+		{ OV511_REG_BUS, 0x5d, 0x03 },
+		{ OV511_REG_BUS, 0x24, 0x9f },
+		{ OV511_REG_BUS, 0x25, 0x90 },
+		{ OV511_REG_BUS, 0x20, 0x00 }, /* Was 0x08 */
+		{ OV511_REG_BUS, 0x51, 0x04 },
+		{ OV511_REG_BUS, 0x71, 0x19 },
 		{ OV511_DONE_BUS, 0x0, 0x00 },
 	};
 
-	memcpy(&ov511->vdev, &ov511_template, sizeof(ov511_template));
+	PDEBUG(4, "");
 
-	for (i = 0; i < OV511_NUMFRAMES; i++)
-		init_waitqueue_head(&ov511->frame[i].wq);
+	/* First 5 bits of custom ID reg are a revision ID on OV518 */
+	info("Device revision %d",
+	     0x1F & ov511_reg_read(dev, OV511_REG_SYSTEM_CUSTOM_ID));
 
-	init_waitqueue_head(&ov511->wq);
+	if (ov511_write_regvals(ov511, aRegvalsInit518)) goto error;
+
+	/* Set LED GPIO pin to output mode */
+	if (ov511_reg_write_mask(dev, 0x57,0x00, 0x02) < 0) goto error;
+
+	/* LED is off by default with OV518; have to explicitly turn it on */
+	if (ov511->led_policy == LED_OFF || ov511->led_policy == LED_AUTO)
+		ov51x_led_control(ov511, 0);
+	else
+		ov51x_led_control(ov511, 1);
+
+	/* Don't require compression if dumppix is enabled; otherwise it's
+	 * required. OV518 has no uncompressed mode, to save RAM. */
+	if (!dumppix && !ov511->compress) {
+		ov511->compress = 1;
+		warn("Compression required with OV518...enabling");
+	}
+
+	if (ov511_write_regvals(ov511, aRegvalsNorm518)) goto error;
 
-	if (ov511_write_regvals(dev, aRegvalsInit)) goto error;
-	if (ov511_write_regvals(dev, aRegvalsNorm511)) goto error;
+	if (ov511_reg_write(dev, 0x2f,0x80) < 0) goto error;
+
+	if (ov518_init_compression(ov511)) goto error;
 
 	ov511_set_packet_size(ov511, 0);
 
-	ov511->snap_enabled = snapshot;	
+	ov511->snap_enabled = snapshot;
 
 	/* Test for 76xx */
-	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE,
-	                   OV7610_I2C_WRITE_ID) < 0)
-		goto error;
-
-	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ,
-	                   OV7610_I2C_READ_ID) < 0)
+	ov511->primary_i2c_slave = OV7xx0_I2C_WRITE_ID;
+	if (ov51x_set_slave_ids(ov511, OV7xx0_I2C_WRITE_ID,
+				OV7xx0_I2C_READ_ID) < 0)
 		goto error;
 
-	if (ov511_reset(dev, OV511_RESET_NOREGS) < 0)
-		goto error;
+	/* The OV518 must be more aggressive about sensor detection since
+	 * I2C write will never fail if the sensor is not present. We have
+	 * to try to initialize the sensor to detect its presence */
 
-	if (ov511_i2c_write(dev, 0x12, 0x80) < 0) {
+	if (ov51x_init_ov_sensor(ov511) < 0) {
 		/* Test for 6xx0 */
-		if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE,
-		                    OV6xx0_I2C_WRITE_ID) < 0)
-			goto error;
-
-		if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ,
-		                    OV6xx0_I2C_READ_ID) < 0)
+		ov511->primary_i2c_slave = OV6xx0_I2C_WRITE_ID;
+		if (ov51x_set_slave_ids(ov511, OV6xx0_I2C_WRITE_ID,
+					OV6xx0_I2C_READ_ID) < 0)
 			goto error;
 
-		if (ov511_reset(dev, OV511_RESET_NOREGS) < 0)
-			goto error;
+		if (ov51x_init_ov_sensor(ov511) < 0) {
+			/* Test for 8xx0 */
+			ov511->primary_i2c_slave = OV8xx0_I2C_WRITE_ID;
+			if (ov51x_set_slave_ids(ov511, OV8xx0_I2C_WRITE_ID,
+						OV8xx0_I2C_READ_ID) < 0)
+				goto error;
 
-		if (ov511_i2c_write(dev, 0x12, 0x80) < 0) {
-			err("Can't determine sensor slave IDs");
-			goto error;
-		}
-		
-		if(ov6xx0_configure(ov511) < 0) {
-			err("failed to configure OV6xx0");
- 			goto error;
+			if (ov51x_init_ov_sensor(ov511) < 0) {
+				err("Can't determine sensor slave IDs");
+ 				goto error;
+			} else {
+				err("Detected unsupported OV8xx0 sensor");
+				goto error;
+			}
+		} else {
+			if (ov6xx0_configure(ov511) < 0) {
+				err("Failed to configure OV6xx0");
+ 				goto error;
+			}
 		}
 	} else {
-		if(ov76xx_configure(ov511) < 0) {
-			err("failed to configure OV76xx");
+		if (ov7xx0_configure(ov511) < 0) {
+			err("Failed to configure OV7xx0");
 	 		goto error;
 		}
 	}
-	
-	/* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used
-	 * (using read() instead). */
-	for (i = 0; i < OV511_NUMFRAMES; i++) {
-		ov511->frame[i].width = ov511->maxwidth;
-		ov511->frame[i].height = ov511->maxheight;
-		ov511->frame[i].depth = 24;
-		ov511->frame[i].bytes_read = 0;
-		ov511->frame[i].segment = 0;
-		ov511->frame[i].format = VIDEO_PALETTE_RGB24;
-		ov511->frame[i].segsize = GET_SEGSIZE(ov511->frame[i].format);
-	}
 
-	/* Initialize to max width/height, RGB24 */
-	if (ov511_mode_init_regs(ov511, ov511->maxwidth, ov511->maxheight,
-				 VIDEO_PALETTE_RGB24, 0) < 0)
-		goto error;
+	// The OV518 cannot go as low as the sensor can
+	ov511->minwidth = 160;
+	ov511->minheight = 120;
 
 	return 0;
-	
+
 error:
-	usb_driver_release_interface(&ov511_driver,
-		&dev->actconfig->interface[ov511->iface]);
+	err("OV518 Config failed");
 
-	return -EBUSY;	
+	return -EBUSY;
 }
 
 
@@ -3245,12 +6687,13 @@
  ***************************************************************************/
 
 static void *
-ov511_probe(struct usb_device *dev, unsigned int ifnum,
-	const struct usb_device_id *id)
+ov51x_probe(struct usb_device *dev, unsigned int ifnum,
+	    const struct usb_device_id *id)
 {
 	struct usb_interface_descriptor *interface;
 	struct usb_ov511 *ov511;
 	int i;
+	int registered = 0;
 
 	PDEBUG(1, "probing for device...");
 
@@ -3271,98 +6714,147 @@
 
 	if ((ov511 = kmalloc(sizeof(*ov511), GFP_KERNEL)) == NULL) {
 		err("couldn't kmalloc ov511 struct");
-		goto error;
+		goto error_unlock;
 	}
 
 	memset(ov511, 0, sizeof(*ov511));
 
 	ov511->dev = dev;
 	ov511->iface = interface->bInterfaceNumber;
+	ov511->led_policy = led;
+	ov511->compress = compress;
+	ov511->lightfreq = lightfreq;
+	ov511->num_inputs = 1;	   /* Video decoder init functs. change this */
+	ov511->stop_during_set = !fastset;
+	ov511->tuner_type = tuner;
+	ov511->backlight = backlight;
+
+	ov511->auto_brt = autobright;
+	ov511->auto_gain = autogain;
+	ov511->auto_exp = autoexp;
 
 	switch (dev->descriptor.idProduct) {
-	case 0x0511:
+	case PROD_OV511:
 		info("USB OV511 camera found");
 		ov511->bridge = BRG_OV511;
+		ov511->bclass = BCL_OV511;
 		break;
-	case 0xA511:
+	case PROD_OV511PLUS:
 		info("USB OV511+ camera found");
 		ov511->bridge = BRG_OV511PLUS;
+		ov511->bclass = BCL_OV511;
 		break;
-	case 0x0002:
-		if (dev->descriptor.idVendor != 0x0813)
+	case PROD_OV518:
+		info("USB OV518 camera found");
+		ov511->bridge = BRG_OV518;
+		ov511->bclass = BCL_OV518;
+		break;
+	case PROD_OV518PLUS:
+		info("USB OV518+ camera found");
+		ov511->bridge = BRG_OV518PLUS;
+		ov511->bclass = BCL_OV518;
+		break;
+	case PROD_ME2CAM:
+		if (dev->descriptor.idVendor != VEND_MATTEL)
 			goto error;
 		info("Intel Play Me2Cam (OV511+) found");
 		ov511->bridge = BRG_OV511PLUS;
+		ov511->bclass = BCL_OV511;
 		break;
 	default:
-		err("Unknown product ID");
-		goto error;
+		err("Unknown product ID 0x%x", dev->descriptor.idProduct);
+		goto error_dealloc;
 	}
 
-	ov511->customid = ov511_reg_read(dev, OV511_REG_SYSTEM_CUSTOM_ID);
-	if (ov511->customid < 0) {
-		err("Unable to read camera bridge registers");
-		goto error;
+	/* Workaround for some applications that want data in RGB
+	 * instead of BGR. */
+	if (force_rgb)
+		info("data format set to RGB");
+
+	init_waitqueue_head(&ov511->wq);
+
+	init_MUTEX(&ov511->lock);	/* to 1 == available */
+	init_MUTEX(&ov511->buf_lock);
+	init_MUTEX(&ov511->param_lock);
+	init_MUTEX(&ov511->i2c_lock);
+	ov511->buf_state = BUF_NOT_ALLOCATED;
+
+	if (ov511->bridge == BRG_OV518 ||
+	    ov511->bridge == BRG_OV518PLUS) {
+		if (ov518_configure(ov511) < 0)
+			goto error;
+	} else {
+		if (ov511_configure(ov511) < 0)
+			goto error;
 	}
 
-	ov511->desc = -1;
-	PDEBUG (4, "CustomID = %d", ov511->customid);
-	for (i = 0; clist[i].id >= 0; i++) {
-		if (ov511->customid == clist[i].id) {
-			info("camera: %s", clist[i].description);
-			ov511->desc = i;
-			break;
-		}
+	for (i = 0; i < OV511_NUMFRAMES; i++) {
+		ov511->frame[i].framenum = i;
+		init_waitqueue_head(&ov511->frame[i].wq);
 	}
 
-	/* Lifeview USB Life TV not supported */
-	if (clist[i].id == 38) {
-		err("This device is not supported yet.");
+	/* Unnecessary? (This is done on open(). Need to make sure variables
+	 * are properly initialized without this before removing it, though). */
+	if (ov51x_set_default_params(ov511) < 0)
 		goto error;
-	}
 
-	if (clist[i].id == -1) {
-		err("Camera type (%d) not recognized", ov511->customid);
-		err("Please contact mwm@i.am to request");
-		err("support for your camera.");
-	}
+#ifdef OV511_DEBUG
+	if (dump_bridge)
+		ov511_dump_regs(dev);
+#endif
 
-	/* Workaround for some applications that want data in RGB
-	 * instead of BGR */
-	if (force_rgb)
-		info("data format set to RGB");
+	memcpy(&ov511->vdev, &ov511_template, sizeof(ov511_template));
+	ov511->vdev.priv = ov511;
 
-	if (!ov511_configure(ov511)) {
-		ov511->user = 0;
-		init_MUTEX(&ov511->lock);	/* to 1 == available */
-		init_MUTEX(&ov511->buf_lock);
-		ov511->buf_state = BUF_NOT_ALLOCATED;
-	} else {
-		err("Failed to configure camera");
-		goto error;
+	for (i = 0; i < OV511_MAX_UNIT_VIDEO; i++) {
+		/* Minor 0 cannot be specified; assume user wants autodetect */
+		if (unit_video[i] == 0)
+			break;
+
+		if (video_register_device(&ov511->vdev, VFL_TYPE_GRABBER,
+			unit_video[i]) >= 0) {
+			registered = 1;
+			break;
+		}
 	}
 
-	if (video_register_device(&ov511->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
+	/* Use the next available one */
+	if (!registered &&
+	    video_register_device(&ov511->vdev, VFL_TYPE_GRABBER, -1) < 0) {
 		err("video_register_device failed");
 		goto error;
 	}
 
+	info("Device registered on minor %d", ov511->vdev.minor);
+
 	MOD_DEC_USE_COUNT;
      	return ov511;
 
 error:
+	err("Camera initialization failed");
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+	/* Safe to call even if entry doesn't exist */
+	destroy_proc_ov511_cam(ov511);
+#endif
+
+	usb_driver_release_interface(&ov511_driver,
+		&dev->actconfig->interface[ov511->iface]);
+
+error_dealloc:
 	if (ov511) {
 		kfree(ov511);
 		ov511 = NULL;
 	}
 
+error_unlock:
 	MOD_DEC_USE_COUNT;
 	return NULL;
 }
 
 
 static void
-ov511_disconnect(struct usb_device *dev, void *ptr)
+ov51x_disconnect(struct usb_device *dev, void *ptr)
 {
 	struct usb_ov511 *ov511 = (struct usb_ov511 *) ptr;
 	int n;
@@ -3422,8 +6914,8 @@
 static struct usb_driver ov511_driver = {
 	name:		"ov511",
 	id_table:       device_table,
-	probe:		ov511_probe,
-	disconnect:	ov511_disconnect
+	probe:		ov51x_probe,
+	disconnect:	ov51x_disconnect
 };
 
 
@@ -3433,7 +6925,88 @@
  *
  ***************************************************************************/
 
-static int __init usb_ov511_init(void)
+/* Returns 0 for success */
+int 
+ov511_register_decomp_module(int ver, struct ov51x_decomp_ops *ops, int ov518,
+			     int mmx)
+{
+	if (ver != DECOMP_INTERFACE_VER) {
+		err("Decompression module has incompatible");
+		err("interface version %d", ver);
+		err("Interface version %d is required", DECOMP_INTERFACE_VER);
+		return -EINVAL;
+	}
+
+	if (!ops)
+		return -EFAULT;
+
+	if (mmx && !ov51x_mmx_available) {
+		err("MMX not available on this system or kernel");
+		return -EINVAL;
+	}
+
+	lock_kernel();
+
+	if (ov518) {
+		if (mmx) {
+			if (ov518_mmx_decomp_ops)
+				goto err_in_use;
+			else
+				ov518_mmx_decomp_ops = ops;
+		} else {
+			if (ov518_decomp_ops)
+				goto err_in_use;
+			else
+				ov518_decomp_ops = ops;
+		}
+	} else {
+		if (mmx) {
+			if (ov511_mmx_decomp_ops)
+				goto err_in_use;
+			else
+				ov511_mmx_decomp_ops = ops;
+		} else {
+			if (ov511_decomp_ops)
+				goto err_in_use;
+			else
+				ov511_decomp_ops = ops;
+		}
+	}
+
+	MOD_INC_USE_COUNT;
+
+	unlock_kernel();
+	return 0;
+
+err_in_use:
+	unlock_kernel();
+	return -EBUSY;
+}
+
+void 
+ov511_deregister_decomp_module(int ov518, int mmx)
+{
+	lock_kernel();
+
+	if (ov518) {
+		if (mmx)
+			ov518_mmx_decomp_ops = NULL;
+		else
+			ov518_decomp_ops = NULL;
+	} else {
+		if (mmx)
+			ov511_mmx_decomp_ops = NULL;
+		else
+			ov511_decomp_ops = NULL;
+	}
+	
+	MOD_DEC_USE_COUNT;
+
+	unlock_kernel();
+}
+
+static int __init 
+usb_ov511_init(void)
 {
 #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
         proc_ov511_create();
@@ -3442,20 +7015,33 @@
 	if (usb_register(&ov511_driver) < 0)
 		return -1;
 
-	info(DRIVER_VERSION ":" DRIVER_DESC);
+	// FIXME: Don't know how to determine this yet
+	ov51x_mmx_available = 0;
+
+#if defined (__i386__)
+	if (test_bit(X86_FEATURE_MMX, &boot_cpu_data.x86_capability))
+		ov51x_mmx_available = 1;
+#endif
+
+	info(DRIVER_VERSION " : " DRIVER_DESC);
 
 	return 0;
 }
 
-static void __exit usb_ov511_exit(void)
+static void __exit 
+usb_ov511_exit(void)
 {
 	usb_deregister(&ov511_driver);
 	info("driver deregistered");
 
 #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
         proc_ov511_destroy();
-#endif 
+#endif
 }
 
 module_init(usb_ov511_init);
 module_exit(usb_ov511_exit);
+
+/* No version, for compatibility with binary-only modules */
+EXPORT_SYMBOL_NOVERS(ov511_register_decomp_module);
+EXPORT_SYMBOL_NOVERS(ov511_deregister_decomp_module);
diff -urN linux-2.5.2-pre7/drivers/usb/ov511.h linux/drivers/usb/ov511.h
--- linux-2.5.2-pre7/drivers/usb/ov511.h	Mon Dec 11 13:17:59 2000
+++ linux/drivers/usb/ov511.h	Fri Jan  4 19:01:21 2002
@@ -1,20 +1,41 @@
-
 #ifndef __LINUX_OV511_H
 #define __LINUX_OV511_H
 
 #include <asm/uaccess.h>
 #include <linux/videodev.h>
 #include <linux/smp_lock.h>
+#include <linux/usb.h>
 
 #define OV511_DEBUG	/* Turn on debug messages */
 
 #ifdef OV511_DEBUG
 #  define PDEBUG(level, fmt, args...) \
-if (debug >= level) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , ## args)
+if (debug >= (level)) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , \
+	## args)
 #else
 #  define PDEBUG(level, fmt, args...) do {} while(0)
 #endif
 
+/* This macro restricts an int variable to an inclusive range */
+#define RESTRICT_TO_RANGE(v,mi,ma) { \
+	if ((v) < (mi)) (v) = (mi); \
+	else if ((v) > (ma)) (v) = (ma); \
+}
+
+/* --------------------------------- */
+/* DEFINES FOR OV511 AND OTHER CHIPS */
+/* --------------------------------- */
+
+/* USB IDs */
+#define VEND_OMNIVISION	0x05A9
+#define PROD_OV511	0x0511
+#define PROD_OV511PLUS	0xA511
+#define PROD_OV518	0x0518
+#define PROD_OV518PLUS	0xA518
+
+#define VEND_MATTEL	0x0813
+#define PROD_ME2CAM	0x0002
+
 /* Camera interface register numbers */
 #define OV511_REG_CAMERA_DELAY_MODE		0x10
 #define OV511_REG_CAMERA_EDGE_MODE		0x11
@@ -52,6 +73,7 @@
 
 /* I2C register numbers */
 #define OV511_REG_I2C_CONTROL			0x40
+#define OV518_REG_I2C_CONTROL			0x47	/* OV518(+) only */
 #define OV511_REG_I2C_SLAVE_ID_WRITE		0x41
 #define OV511_REG_I2C_SUB_ADDRESS_3_BYTE	0x42
 #define OV511_REG_I2C_SUB_ADDRESS_2_BYTE	0x43
@@ -78,8 +100,16 @@
 #define OV511_REG_SYSTEM_CLOCK_DIVISOR		0x51
 #define OV511_REG_SYSTEM_SNAPSHOT		0x52
 #define OV511_REG_SYSTEM_INIT         		0x53
-#define OV511_REG_SYSTEM_PWR_CLK		0x54	/* OV511+ only */
+#define OV511_REG_SYSTEM_PWR_CLK		0x54 /* OV511+/OV518(+) only */
 #define OV511_REG_SYSTEM_LED_CTL		0x55	/* OV511+ only */
+#define OV518_REG_GPIO_IN			0x55	/* OV518(+) only */
+#define OV518_REG_GPIO_OUT			0x56	/* OV518(+) only */
+#define OV518_REG_GPIO_CTL			0x57	/* OV518(+) only */
+#define OV518_REG_GPIO_PULSE_IN			0x58	/* OV518(+) only */
+#define OV518_REG_GPIO_PULSE_CLEAR		0x59	/* OV518(+) only */
+#define OV518_REG_GPIO_PULSE_POLARITY		0x5a	/* OV518(+) only */
+#define OV518_REG_GPIO_PULSE_EN			0x5b	/* OV518(+) only */
+#define OV518_REG_GPIO_RESET			0x5c	/* OV518(+) only */
 #define OV511_REG_SYSTEM_USER_DEFINED		0x5E
 #define OV511_REG_SYSTEM_CUSTOM_ID		0x5F
 
@@ -119,6 +149,16 @@
 #define OV511PLUS_ALT_SIZE_769	6
 #define OV511PLUS_ALT_SIZE_961	7
 
+/* Alternate numbers for various max packet sizes (OV518(+) only) */
+#define OV518_ALT_SIZE_0	0
+#define OV518_ALT_SIZE_128	1
+#define OV518_ALT_SIZE_256	2
+#define OV518_ALT_SIZE_384	3
+#define OV518_ALT_SIZE_512	4
+#define OV518_ALT_SIZE_640	5
+#define OV518_ALT_SIZE_768	6
+#define OV518_ALT_SIZE_896	7
+
 /* OV7610 registers */
 #define OV7610_REG_GAIN          0x00	/* gain setting (5:0) */
 #define OV7610_REG_BLUE          0x01	/* blue channel balance */
@@ -170,44 +210,74 @@
 					/* 36-37 reserved */
 #define OV7610_REG_COM_K         0x38	/* misc registers */
 
-
-#define SCRATCH_BUF_SIZE 512
-
 #define FRAMES_PER_DESC		10	/* FIXME - What should this be? */
 #define FRAME_SIZE_PER_DESC	993	/* FIXME - Deprecated */
 #define MAX_FRAME_SIZE_PER_DESC	993	/* For statically allocated stuff */
+#define PIXELS_PER_SEG		256	/* Pixels per segment */
 
 #define OV511_ENDPOINT_ADDRESS 1	/* Isoc endpoint number */
 
-// CAMERA SPECIFIC
-// FIXME - these can vary between specific models
-#define OV7610_I2C_WRITE_ID 0x42
-#define OV7610_I2C_READ_ID  0x43
-#define OV6xx0_I2C_WRITE_ID 0xC0
-#define OV6xx0_I2C_READ_ID  0xC1
+/* I2C addresses */
+#define OV7xx0_I2C_WRITE_ID   0x42
+#define OV7xx0_I2C_READ_ID    0x43
+#define OV6xx0_I2C_WRITE_ID   0xC0
+#define OV6xx0_I2C_READ_ID    0xC1
+#define OV8xx0_I2C_WRITE_ID   0xA0
+#define OV8xx0_I2C_READ_ID    0xA1
+#define KS0127_I2C_WRITE_ID   0xD8
+#define KS0127_I2C_READ_ID    0xD9
+#define SAA7111A_I2C_WRITE_ID 0x48
+#define SAA7111A_I2C_READ_ID  0x49
 
 #define OV511_I2C_CLOCK_PRESCALER 0x03
 
-/* Prototypes */
-int usb_ov511_reg_read(struct usb_device *dev, unsigned char reg);
-int usb_ov511_reg_write(struct usb_device *dev,
-                        unsigned char reg,
-                        unsigned char value);
-
 /* Bridge types */
 enum {
+	BRG_UNKNOWN,
 	BRG_OV511,
 	BRG_OV511PLUS,
+	BRG_OV518,
+	BRG_OV518PLUS,
+};
+
+/* Bridge classes */
+enum {
+	BCL_UNKNOWN,
+	BCL_OV511,
+	BCL_OV518,
 };
 
 /* Sensor types */
 enum {
 	SEN_UNKNOWN,
+	SEN_OV76BE,
 	SEN_OV7610,
 	SEN_OV7620,
 	SEN_OV7620AE,
 	SEN_OV6620,
+	SEN_OV6630,
+	SEN_OV6630AE,
+	SEN_OV6630AF,
+	SEN_OV8600,
+	SEN_KS0127,
+	SEN_KS0127B,
+	SEN_SAA7111A,
+};
+
+// Not implemented yet
+#if 0
+/* Sensor classes */
+enum {
+	SCL_UNKNOWN,
+	SCL_OV7610,	/* 7610, 76BE, 7620AE (for now) */
+	SCL_OV7620,
+	SCL_OV6620,	
+	SCL_OV6630,	/* 6630, 6630AE, 6630AF */
+	SCL_OV8600,
+	SCL_KS0127,	/* SEN_KS0127, SEN_KS0127B */
+	SCL_SAA7111A,
 };
+#endif
 
 enum {
 	STATE_SCANNING,		/* Scanning for start */
@@ -222,7 +292,77 @@
 	BUF_PEND_DEALLOC,	/* ov511->buf_timer is set */
 };
 
-struct usb_device;
+/* --------- Definition of ioctl interface --------- */
+
+#define OV511_INTERFACE_VER 101
+
+/* LED options */
+enum {
+	LED_OFF,
+	LED_ON,
+	LED_AUTO,
+};
+
+/* Raw frame formats */
+enum {
+	RAWFMT_INVALID,
+	RAWFMT_YUV400,
+	RAWFMT_YUV420,
+	RAWFMT_YUV422,
+	RAWFMT_GBR422,
+};
+
+/* Unsigned short option numbers */
+enum {
+	OV511_USOPT_INVALID,
+	OV511_USOPT_BRIGHT,
+	OV511_USOPT_SAT,
+	OV511_USOPT_HUE,
+	OV511_USOPT_CONTRAST,
+};
+
+/* Unsigned int option numbers */
+enum {
+	OV511_UIOPT_INVALID,
+	OV511_UIOPT_POWER_FREQ,
+	OV511_UIOPT_BFILTER,
+	OV511_UIOPT_LED,
+	OV511_UIOPT_DEBUG,
+	OV511_UIOPT_COMPRESS,
+};
+
+struct ov511_ushort_opt {
+	int optnum;		/* Specific option number */
+	unsigned short val;
+};
+
+struct ov511_uint_opt {
+	int optnum;		/* Specific option number */
+	unsigned int val;
+};
+
+struct ov511_i2c_struct {
+	unsigned char slave; /* Write slave ID (read ID - 1) */
+	unsigned char reg;   /* Index of register */
+	unsigned char value; /* User sets this w/ write, driver does w/ read */
+	unsigned char mask;  /* Bits to be changed. Not used with read ops */
+};
+
+/* ioctls */
+#define OV511IOC_GINTVER  _IOR('v', BASE_VIDIOCPRIVATE + 0, int)
+#define OV511IOC_GUSHORT _IOWR('v', BASE_VIDIOCPRIVATE + 1, \
+			       struct ov511_ushort_opt)
+#define OV511IOC_SUSHORT  _IOW('v', BASE_VIDIOCPRIVATE + 2, \
+			       struct ov511_ushort_opt)
+#define OV511IOC_GUINT   _IOWR('v', BASE_VIDIOCPRIVATE + 3, \
+			       struct ov511_uint_opt)
+#define OV511IOC_SUINT    _IOW('v', BASE_VIDIOCPRIVATE + 4, \
+			       struct ov511_uint_opt)
+#define OV511IOC_WI2C     _IOW('v', BASE_VIDIOCPRIVATE + 5, \
+			       struct ov511_i2c_struct)
+#define OV511IOC_RI2C    _IOWR('v', BASE_VIDIOCPRIVATE + 6, \
+			       struct ov511_i2c_struct)
+/* ------------- End IOCTL interface -------------- */
 
 struct ov511_sbuf {
 	char *data;
@@ -248,36 +388,51 @@
 };
 
 struct ov511_frame {
+	int framenum;		/* Index of this frame */
 	char *data;		/* Frame buffer */
+	char *tempdata;		/* Temp buffer for multi-stage conversions */
+	char *rawdata;		/* Raw camera data buffer */
 
 	int depth;		/* Bytes per pixel */
 	int width;		/* Width application is expecting */
-	int height;		/* Height */
+	int height;		/* Height application is expecting */
 
-	int hdrwidth;		/* Width the frame actually is */
-	int hdrheight;		/* Height */
+	int rawwidth;		/* Actual width of frame sent from camera */
+	int rawheight;		/* Actual height of frame sent from camera */
 
 	int sub_flag;		/* Sub-capture mode for this frame? */
 	unsigned int format;	/* Format for this frame */
-	int segsize;		/* How big is each segment from the camera? */
+	int compressed;		/* Is frame compressed? */
 
 	volatile int grabstate;	/* State of grabbing */
 	int scanstate;		/* State of scanning */
 
-	int curline;		/* Line of frame we're working on */
-	int curpix;
-	int segment;		/* Segment from the incoming data */
+	int bytes_recvd;	/* Number of image bytes received from camera */
 
-	long scanlength;	/* uncompressed, raw data length of frame */
-	long bytes_read;	/* amount of scanlength that has been read from *data */
+	long bytes_read;	/* Amount that has been read() */
 
 	wait_queue_head_t wq;	/* Processes waiting */
 
 	int snapshot;		/* True if frame was a snapshot */
 };
 
+#define DECOMP_INTERFACE_VER 2
+
+/* Compression module operations */
+struct ov51x_decomp_ops {
+	int (*decomp_400)(unsigned char *, unsigned char *, int, int, int);
+	int (*decomp_420)(unsigned char *, unsigned char *, int, int, int);
+	int (*decomp_422)(unsigned char *, unsigned char *, int, int, int);
+	void (*decomp_lock)(void);
+	void (*decomp_unlock)(void);
+};
+
 #define OV511_NUMFRAMES	2
-#define OV511_NUMSBUF	2
+#if OV511_NUMFRAMES > VIDEO_MAX_FRAME
+#error "OV511_NUMFRAMES is too high"
+#endif
+
+#define OV511_NUMSBUF		2
 
 struct usb_ov511 {
 	struct video_device vdev;
@@ -292,22 +447,37 @@
 	/* Determined by sensor type */
 	int maxwidth;
 	int maxheight;
+	int minwidth;
+	int minheight;
 
 	int brightness;
 	int colour;
 	int contrast;
 	int hue;
 	int whiteness;
+	int exposure;
+	int auto_brt;		/* Auto brightness enabled flag */
+	int auto_gain;		/* Auto gain control enabled flag */
+	int auto_exp;		/* Auto exposure enabled flag */
+	int backlight;		/* Backlight exposure algorithm flag */
 
-	struct semaphore lock;
+	int led_policy;		/* LED: off|on|auto; OV511+ only */
+
+	struct semaphore lock;	/* Serializes user-accessible operations */
 	int user;		/* user count for exclusive use */
 
 	int streaming;		/* Are we streaming Isochronous? */
 	int grabbing;		/* Are we grabbing? */
 
 	int compress;		/* Should the next frame be compressed? */
+	int compress_inited;	/* Are compression params uploaded? */
+
+	int lightfreq;		/* Power (lighting) frequency */
+	int bandfilt;		/* Banding filter enabled flag */
 
 	char *fbuf;		/* Videodev buffer area */
+	char *tempfbuf;		/* Temporary (intermediate) buffer area */
+	char *rawfbuf;		/* Raw camera data buffer area */
 
 	int sub_flag;		/* Pix Array subcapture on flag */
 	int subx;		/* Pix Array subcapture x offset */
@@ -318,30 +488,53 @@
 	int curframe;		/* Current receiving sbuf */
 	struct ov511_frame frame[OV511_NUMFRAMES];	
 
-	int cursbuf;		/* Current receiving sbuf */
 	struct ov511_sbuf sbuf[OV511_NUMSBUF];
 
-	/* Scratch space from the Isochronous pipe */
-	unsigned char scratch[SCRATCH_BUF_SIZE];
-	int scratchlen;
-
 	wait_queue_head_t wq;	/* Processes waiting */
 
 	int snap_enabled;	/* Snapshot mode enabled */
 	
-	int bridge;		/* Type of bridge (OV511 or OV511+) */
-	int sensor;		/* Type of image sensor chip */
+	int bridge;		/* Type of bridge (BRG_*) */
+	int bclass;		/* Class of bridge (BCL_*) */
+	int sensor;		/* Type of image sensor chip (SEN_*) */
+	int sclass;		/* Type of image sensor chip (SCL_*) */
+	int tuner;		/* Type of TV tuner */
 
 	int packet_size;	/* Frame size per isoc desc */
 
-				/* proc interface */
 	struct semaphore param_lock;	/* params lock for this camera */
-	struct proc_dir_entry *proc_entry;	/* /proc/ov511/videoX */
-	
+
+	/* /proc entries, relative to /proc/video/ov511/ */
+	struct proc_dir_entry *proc_devdir;   /* Per-device proc directory */
+	struct proc_dir_entry *proc_info;     /* <minor#>/info entry */
+	struct proc_dir_entry *proc_button;   /* <minor#>/button entry */
+	struct proc_dir_entry *proc_control;  /* <minor#>/control entry */
+
 	/* Framebuffer/sbuf management */
 	int buf_state;
 	struct semaphore buf_lock;
 	struct timer_list buf_timer;
+
+	struct ov51x_decomp_ops *decomp_ops;
+
+	/* Stop streaming while changing picture settings */
+	int stop_during_set;
+
+	int stopped;		/* Streaming is temporarily paused */
+
+	/* Video decoder stuff */
+	int input;		/* Composite, S-VIDEO, etc... */
+	int num_inputs;		/* Number of inputs */
+	int norm; 		/* NTSC / PAL / SECAM */
+	int has_decoder;	/* Device has a video decoder */
+	int has_tuner;		/* Device has a TV tuner */
+	int has_audio_proc;	/* Device has an audio processor */
+	int freq;		/* Current tuner frequency */
+	int tuner_type;		/* Specific tuner model */
+
+	/* I2C interface to kernel */
+	struct semaphore i2c_lock;	  /* Protect I2C controller regs */
+	unsigned char primary_i2c_slave;  /* I2C write id of sensor */
 };
 
 struct cam_list {
@@ -354,18 +547,57 @@
 	char *name;
 };
 
-struct mode_list {
+struct mode_list_518 {
 	int width;
 	int height;
-	int color;		/* 0=grayscale, 1=color */
-	u8 pxcnt;		/* pixel counter */
-	u8 lncnt;		/* line counter */
-	u8 pxdv;		/* pixel divisor */
-	u8 lndv;		/* line divisor */
-	u8 m420;
-	u8 common_A;
-	u8 common_L;
-};
+	u8 reg28;
+	u8 reg29;
+	u8 reg2a;
+	u8 reg2c;
+	u8 reg2e;
+	u8 reg24;
+	u8 reg25;
+};
+
+/* Compression stuff */
+
+#define OV511_QUANTABLESIZE	64
+#define OV518_QUANTABLESIZE	32
+
+#define OV511_YQUANTABLE { \
+	0, 1, 1, 2, 2, 3, 3, 4, \
+	1, 1, 1, 2, 2, 3, 4, 4, \
+	1, 1, 2, 2, 3, 4, 4, 4, \
+	2, 2, 2, 3, 4, 4, 4, 4, \
+	2, 2, 3, 4, 4, 5, 5, 5, \
+	3, 3, 4, 4, 5, 5, 5, 5, \
+	3, 4, 4, 4, 5, 5, 5, 5, \
+	4, 4, 4, 4, 5, 5, 5, 5  \
+}
+
+#define OV511_UVQUANTABLE { \
+	0, 2, 2, 3, 4, 4, 4, 4, \
+	2, 2, 2, 4, 4, 4, 4, 4, \
+	2, 2, 3, 4, 4, 4, 4, 4, \
+	3, 4, 4, 4, 4, 4, 4, 4, \
+	4, 4, 4, 4, 4, 4, 4, 4, \
+	4, 4, 4, 4, 4, 4, 4, 4, \
+	4, 4, 4, 4, 4, 4, 4, 4, \
+	4, 4, 4, 4, 4, 4, 4, 4  \
+}
+
+#define OV518_YQUANTABLE { \
+	5, 4, 5, 6, 6, 7, 7, 7, \
+	5, 5, 5, 5, 6, 7, 7, 7, \
+	6, 6, 6, 6, 7, 7, 7, 8, \
+	7, 7, 6, 7, 7, 7, 8, 8  \
+}
+
+#define OV518_UVQUANTABLE { \
+	6, 6, 6, 7, 7, 7, 7, 7, \
+	6, 6, 6, 7, 7, 7, 7, 7, \
+	6, 6, 6, 7, 7, 7, 7, 8, \
+	7, 7, 7, 7, 7, 7, 8, 8  \
+}
 
 #endif
-
diff -urN linux-2.5.2-pre7/drivers/usb/pwc-ctrl.c linux/drivers/usb/pwc-ctrl.c
--- linux-2.5.2-pre7/drivers/usb/pwc-ctrl.c	Mon Nov 26 17:09:10 2001
+++ linux/drivers/usb/pwc-ctrl.c	Fri Jan  4 19:01:22 2002
@@ -1008,6 +1008,8 @@
 
 	if (pdev->type < 730)
 		return 0;
+	on_value /= 100;
+	off_value /= 100;
 	if (on_value < 0)
 		on_value = 0;
 	if (on_value > 0xff)
@@ -1048,8 +1050,8 @@
 
 	if (ret < 0)
 		return ret;
-	*on_value = buf[0];
-	*off_value = buf[1];
+	*on_value = buf[0] * 100;
+	*off_value = buf[1] * 100;
 	return 0;
 }
 
@@ -1175,6 +1177,8 @@
 			wb.read_red = pwc_read_red_gain(pdev);
 			wb.read_blue = pwc_read_blue_gain(pdev);
 		}
+		if (copy_to_user(arg, &wb, sizeof(wb)))
+			return -EFAULT;
 		break;
 	}
 
diff -urN linux-2.5.2-pre7/drivers/usb/pwc-if.c linux/drivers/usb/pwc-if.c
--- linux-2.5.2-pre7/drivers/usb/pwc-if.c	Fri Jan  4 19:01:17 2002
+++ linux/drivers/usb/pwc-if.c	Fri Jan  4 19:01:22 2002
@@ -39,7 +39,10 @@
 
 /* Contributors:
    - Alvarado: adding whitebalance code
-   - Alistar Moire: QuickCam 3000 Pro testing
+   - Alistar Moire: QuickCam 3000 Pro device/product ID
+   - Tony Hoyle: Creative Labs Webcam 5 device/product ID
+   - Mark Burazin: solving hang in VIDIOCSYNC when camera gets unplugged
+   - Jk Fang: SOTEC device/product ID
 */
 
 #include <linux/errno.h>
@@ -76,6 +79,8 @@
 	{ USB_DEVICE(0x046D, 0x08b0) },
 	{ USB_DEVICE(0x055D, 0x9000) },
 	{ USB_DEVICE(0x055D, 0x9001) },
+	{ USB_DEVICE(0x041E, 0x400C) },
+	{ USB_DEVICE(0x04CC, 0x8116) },
 	{ }
 };
 MODULE_DEVICE_TABLE(usb, pwc_device_table);
@@ -887,6 +892,7 @@
 	/* Stop camera, but only if we are sure the camera is still there */
 	if (!pdev->unplugged)
 		usb_set_interface(pdev->udev, 0, 0);
+	/* Unlinking ISOC buffers one by one */
 	for (i = MAX_ISO_BUFS - 1; i >= 0; i--) {
 		pdev->sbuf[i].urb->next = NULL;
 		usb_unlink_urb(pdev->sbuf[i].urb);
@@ -1493,6 +1499,12 @@
 			 */
 			add_wait_queue(&pdev->frameq, &wait);
 			while (pdev->full_frames == NULL) {
+				if (pdev->unplugged) {
+					remove_wait_queue(&pdev->frameq, &wait);
+					set_current_state(TASK_RUNNING);
+					return -ENODEV;
+				}
+			
 	                	if (signal_pending(current)) {
 	                		remove_wait_queue(&pdev->frameq, &wait);
 		                	set_current_state(TASK_RUNNING);
@@ -1710,7 +1722,29 @@
 			break;
 		}
 	}
-	else return NULL; /* Not Philips, Askey, Logitech or Samsung, for sure. */
+	else if (vendor_id == 0x041e) {
+		switch(product_id) {
+		case 0x400c:
+			Info("Creative Labs Webcam 5 detected.\n");
+			type_id = 730;
+			break;
+		default:
+			return NULL;
+			break;
+		}
+	}
+	else if (vendor_id == 0x04cc) { 
+		switch(product_id) {
+		case 0x8116:
+			Info("SOTEC CMS-001 USB webcam detected.\n");
+			type_id = 730;
+			break;  
+		default:
+			return NULL;
+			break;
+		}
+	}
+	else return NULL; /* Not Philips, Askey, Logitech, Samsung, Creative or SOTEC, for sure. */
 
 	memset(serial_number, 0, 30);
 	usb_string(udev, udev->descriptor.iSerialNumber, serial_number, 29);
@@ -1780,16 +1814,6 @@
 	if (hint < MAX_DEV_HINTS) 
 		device_hint[hint].pdev = pdev;
 
-#if 0
-	/* Shut down camera now (some people like the LED off) */
-	if (power_save) {
-		Trace(TRACE_PROBE, "Powering down camera");
-		i = pwc_camera_power(pdev, 0);
-		if (i < 0)
-			Info("Failed to power-down the camera (%d)\n", i);
-	}
-#endif
-
 	Trace(TRACE_PROBE, "probe() function returning struct at 0x%p.\n", pdev);
 	return pdev;
 }
@@ -1799,6 +1823,7 @@
 {
 	struct pwc_device *pdev;
 	int hint;
+	DECLARE_WAITQUEUE(wait, current);
 
 	lock_kernel();
 	free_mem_leak();
@@ -1833,13 +1858,19 @@
 			 */
 			wake_up(&pdev->frameq);
 			
-			/* Wait until we get a 'go' from _close(). This
-			   had a gigantic race condition, since we kfree()
+			/* Wait until we get a 'go' from _close(). This used
+			   to have a gigantic race condition, since we kfree()
 			   stuff here, but we have to wait until close() 
-			   is finished. */
+			   is finished. 
+			 */
 			   
 			Trace(TRACE_PROBE, "Sleeping on remove_ok.\n");
-			sleep_on(&pdev->remove_ok);
+			add_wait_queue(&pdev->remove_ok, &wait);
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			/* ... wait ... */
+			schedule();
+			remove_wait_queue(&pdev->remove_ok, &wait);
+			set_current_state(TASK_RUNNING);
 			Trace(TRACE_PROBE, "Done sleeping.\n");
 			set_mem_leak(pdev->vdev);
 			pdev->vdev = NULL;
@@ -1920,7 +1951,7 @@
 	char *sizenames[PSZ_MAX] = { "sqcif", "qsif", "qcif", "sif", "cif", "vga" };
 
 	Info("Philips PCA645/646 + PCVC675/680/690 + PCVC730/740/750 webcam module version " PWC_VERSION " loaded.\n");
-	Info("Also supports the Askey VC010, Logitech Quickcam 3000 Pro and the Samsung MPC-C10 and MPC-C30.\n");
+	Info("Also supports the Askey VC010, Logitech Quickcam 3000 Pro, Samsung MPC-C10 and MPC-C30, the Creative WebCam 5 and the SOTEC CMS-001.\n");
 
 	if (fps) {
 		if (fps < 5 || fps > 30) {
diff -urN linux-2.5.2-pre7/drivers/usb/pwc-ioctl.h linux/drivers/usb/pwc-ioctl.h
--- linux-2.5.2-pre7/drivers/usb/pwc-ioctl.h	Wed Oct 17 14:34:06 2001
+++ linux/drivers/usb/pwc-ioctl.h	Fri Jan  4 19:01:22 2002
@@ -79,8 +79,8 @@
 /* Used with VIDIOCPWC[SG]LED */
 struct pwc_leds
 {
-	int led_on;			/* Led on-time; range = 0..255 */
-	int led_off;			/*  */
+	int led_on;			/* Led on-time; range = 0..25000 */
+	int led_off;			/* Led off-time; range = 0..25000  */
 };
 
 
diff -urN linux-2.5.2-pre7/drivers/usb/pwc.h linux/drivers/usb/pwc.h
--- linux-2.5.2-pre7/drivers/usb/pwc.h	Fri Jan  4 19:01:17 2002
+++ linux/drivers/usb/pwc.h	Fri Jan  4 19:01:22 2002
@@ -60,8 +60,8 @@
 
 /* Version block */
 #define PWC_MAJOR	8
-#define PWC_MINOR	4
-#define PWC_VERSION 	"8.4"
+#define PWC_MINOR	5
+#define PWC_VERSION 	"8.5"
 #define PWC_NAME 	"pwc"
 
 /* Turn certain features on/off */
diff -urN linux-2.5.2-pre7/drivers/usb/serial/usb-serial.h linux/drivers/usb/serial/usb-serial.h
--- linux-2.5.2-pre7/drivers/usb/serial/usb-serial.h	Sat Dec  8 20:28:45 2001
+++ linux/drivers/usb/serial/usb-serial.h	Fri Jan  4 19:01:22 2002
@@ -116,6 +116,7 @@
 
 /**
  * usb_serial_device_type - a structure that defines a usb serial device
+ * @owner: pointer to the module that owns this device.
  * @name: pointer to a string that describes this device.  This string used
  *	in the syslog messages when a device is inserted or removed.
  * @id_table: pointer to a list of usb_device_id structures that define all
@@ -138,6 +139,7 @@
  * called, the generic serial function will be used instead.
  */
 struct usb_serial_device_type {
+	struct module *owner;
 	char	*name;
 	const struct usb_device_id *id_table;
 	char	num_interrupt_in;
diff -urN linux-2.5.2-pre7/drivers/usb/serial/usbserial.c linux/drivers/usb/serial/usbserial.c
--- linux-2.5.2-pre7/drivers/usb/serial/usbserial.c	Fri Jan  4 19:01:17 2002
+++ linux/drivers/usb/serial/usbserial.c	Fri Jan  4 19:01:22 2002
@@ -506,6 +506,7 @@
 	struct usb_serial *serial;
 	struct usb_serial_port *port;
 	unsigned int portNumber;
+	int retval;
 	
 	dbg(__FUNCTION__);
 
@@ -527,10 +528,16 @@
 	 
 	/* pass on to the driver specific version of this function if it is available */
 	if (serial->type->open) {
-		return (serial->type->open(port, filp));
+		if (serial->type->owner)
+			__MOD_INC_USE_COUNT(serial->type->owner);
+		retval = serial->type->open(port, filp);
+		if (retval)
+			__MOD_DEC_USE_COUNT(serial->type->owner);
 	} else {
-		return (generic_open(port, filp));
+		retval = generic_open(port, filp);
 	}
+
+	return retval;
 }
 
 
@@ -553,6 +560,8 @@
 	/* pass on to the driver specific version of this function if it is available */
 	if (serial->type->close) {
 		serial->type->close(port, filp);
+		if (serial->type->owner)
+			__MOD_DEC_USE_COUNT(serial->type->owner);
 	} else {
 		generic_close(port, filp);
 	}
@@ -1059,6 +1068,7 @@
 	struct usb_endpoint_descriptor *bulk_out_endpoint[MAX_NUM_PORTS];
 	struct usb_serial_device_type *type = NULL;
 	struct list_head *tmp;
+	int retval;
 	int found;
 	int minor;
 	int buffer_size;
@@ -1180,9 +1190,13 @@
 
 	/* if this device type has a startup function, call it */
 	if (type->startup) {
-		if (type->startup (serial)) {
+		if (type->owner)
+			__MOD_INC_USE_COUNT(type->owner);
+		retval = type->startup (serial);
+		if (type->owner)
+			__MOD_DEC_USE_COUNT(type->owner);
+		if (retval)
 			goto probe_error;
-		}
 	}
 
 	/* set up the endpoint information */
diff -urN linux-2.5.2-pre7/drivers/usb/serial/visor.c linux/drivers/usb/serial/visor.c
--- linux-2.5.2-pre7/drivers/usb/serial/visor.c	Sat Dec  8 20:28:45 2001
+++ linux/drivers/usb/serial/visor.c	Fri Jan  4 19:01:22 2002
@@ -12,6 +12,10 @@
  *
  * See Documentation/usb/usb-serial.txt for more information on using this driver
  * 
+ * (12/18/2001) gkh
+ *	Added better Clie support for 3.5 devices.  Thanks to Geoffrey Levand
+ *	for the patch.
+ *
  * (11/11/2001) gkh
  *	Added support for the m125 devices, and added check to prevent oopses
  *	for Clié devices that lie about the number of ports they have.
@@ -127,7 +131,7 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v1.7"
+#define DRIVER_VERSION "v1.8"
 #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>"
 #define DRIVER_DESC "USB HandSpring Visor, Palm m50x, Sony Clié driver"
 
@@ -145,6 +149,7 @@
 static void visor_set_termios	(struct usb_serial_port *port, struct termios *old_termios);
 static void visor_write_bulk_callback	(struct urb *urb);
 static void visor_read_bulk_callback	(struct urb *urb);
+static int  clie_3_5_startup	(struct usb_serial *serial);
 
 
 static __devinitdata struct usb_device_id combined_id_table [] = {
@@ -177,6 +182,7 @@
 
 /* All of the device info needed for the Handspring Visor, and Palm 4.0 devices */
 static struct usb_serial_device_type handspring_device = {
+	owner:			THIS_MODULE,
 	name:			"Handspring Visor / Palm 4.0 / Clié 4.0",
 	id_table:		combined_id_table,
 	num_interrupt_in:	0,
@@ -200,6 +206,7 @@
 
 /* device info for the Sony Clie OS version 3.5 */
 static struct usb_serial_device_type clie_3_5_device = {
+	owner:			THIS_MODULE,
 	name:			"Sony Clié 3.5",
 	id_table:		clie_id_3_5_table,
 	num_interrupt_in:	0,
@@ -210,6 +217,7 @@
 	close:			visor_close,
 	throttle:		visor_throttle,
 	unthrottle:		visor_unthrottle,
+	startup:		clie_3_5_startup,
 	ioctl:			visor_ioctl,
 	set_termios:		visor_set_termios,
 	write:			visor_write,
@@ -249,7 +257,6 @@
 	down (&port->sem);
 	
 	++port->open_count;
-	MOD_INC_USE_COUNT;
 	
 	if (port->open_count == 1) {
 		bytes_in = 0;
@@ -321,8 +328,6 @@
 
 	/* Uncomment the following line if you want to see some statistics in your syslog */
 	/* info ("Bytes In = %d  Bytes Out = %d", bytes_in, bytes_out); */
-
-	MOD_DEC_USE_COUNT;
 }
 
 
@@ -644,6 +649,46 @@
 	return 0;
 }
 
+static int clie_3_5_startup (struct usb_serial *serial)
+{
+	int result;
+	u8 data;
+
+	dbg(__FUNCTION__);
+
+	/*
+	 * Note that PEG-300 series devices expect the following two calls.
+	 */
+
+	/* get the config number */
+	result = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+				  USB_REQ_GET_CONFIGURATION, USB_DIR_IN,
+				  0, 0, &data, 1, HZ * 3);
+	if (result < 0) {
+		err(__FUNCTION__ ": get config number failed: %d", result);
+		return result;
+	}
+	if (result != 1) {
+		err(__FUNCTION__ ": get config number bad return length: %d", result);
+		return -EIO;
+	}
+
+	/* get the interface number */
+	result = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+				  USB_REQ_GET_INTERFACE, 
+				  USB_DIR_IN | USB_DT_DEVICE,
+				  0, 0, &data, 1, HZ * 3);
+	if (result < 0) {
+		err(__FUNCTION__ ": get interface number failed: %d", result);
+		return result;
+	}
+	if (result != 1) {
+		err(__FUNCTION__ ": get interface number bad return length: %d", result);
+		return -EIO;
+	}
+
+	return 0;
+}
 
 static void visor_shutdown (struct usb_serial *serial)
 {
diff -urN linux-2.5.2-pre7/drivers/usb/storage/datafab.c linux/drivers/usb/storage/datafab.c
--- linux-2.5.2-pre7/drivers/usb/storage/datafab.c	Tue Oct  9 15:15:02 2001
+++ linux/drivers/usb/storage/datafab.c	Fri Jan  4 19:01:22 2002
@@ -208,7 +208,7 @@
 
 		if (use_sg) {
 			sg = (struct scatterlist *) dest;
-			buffer = kmalloc(len, GFP_KERNEL);
+			buffer = kmalloc(len, GFP_NOIO);
 			if (buffer == NULL)
 				return USB_STOR_TRANSPORT_ERROR;
 			ptr = buffer;
@@ -333,7 +333,7 @@
 
 		if (use_sg) {
 			sg = (struct scatterlist *) src;
-			buffer = kmalloc(len, GFP_KERNEL);
+			buffer = kmalloc(len, GFP_NOIO);
 			if (buffer == NULL)
 				return USB_STOR_TRANSPORT_ERROR;
 			ptr = buffer;
@@ -665,7 +665,7 @@
 	};
 
 	if (!us->extra) {
-		us->extra = kmalloc(sizeof(struct datafab_info), GFP_KERNEL);
+		us->extra = kmalloc(sizeof(struct datafab_info), GFP_NOIO);
 		if (!us->extra) {
 			US_DEBUGP("datafab_transport:  Gah! Can't allocate storage for Datafab info struct!\n");
 			return USB_STOR_TRANSPORT_ERROR;
diff -urN linux-2.5.2-pre7/drivers/usb/storage/freecom.c linux/drivers/usb/storage/freecom.c
--- linux-2.5.2-pre7/drivers/usb/storage/freecom.c	Tue Nov 13 09:19:41 2001
+++ linux/drivers/usb/storage/freecom.c	Fri Jan  4 19:01:22 2002
@@ -1,6 +1,6 @@
 /* Driver for Freecom USB/IDE adaptor
  *
- * $Id: freecom.c,v 1.19 2001/11/11 05:42:34 mdharm Exp $
+ * $Id: freecom.c,v 1.21 2001/12/29 03:47:33 mdharm Exp $
  *
  * Freecom v0.1:
  *
@@ -206,9 +206,7 @@
 
         return USB_STOR_TRANSPORT_GOOD;
 }
-#endif
 
-#if 0 /* Unused at this time */
 /* Read a value from an ide register. */
 static int
 freecom_ide_read (struct us_data *us, int reg, int *value)
@@ -435,7 +433,7 @@
 		/* Get the status again */
 		fcb->Type = FCM_PACKET_STATUS;
 		fcb->Timeout = 0;
-		memset (fcb->Atapi, 0, sizeof(fcb->Filler));
+		memset (fcb->Atapi, 0, sizeof(fcb->Atapi));
 		memset (fcb->Filler, 0, sizeof (fcb->Filler));
 
         	/* Send it out. */
@@ -487,10 +485,19 @@
          * and such will hang. */
         US_DEBUGP("Device indicates that it has %d bytes available\n",
                         le16_to_cpu (fst->Count));
+        US_DEBUGP("SCSI requested %d\n", usb_stor_transfer_length(srb));
 
         /* Find the length we desire to read. */
-        length = usb_stor_transfer_length (srb);
-        US_DEBUGP("SCSI requested %d\n", length);
+	switch (srb->cmnd[0]) {
+		case INQUIRY:
+		case REQUEST_SENSE:		/* 16 or 18 bytes? spec says 18, lots of devices only have 16 */
+		case MODE_SENSE:
+		case MODE_SENSE_10:
+			length = fst->Count;
+			break;
+		default:
+ 			length = usb_stor_transfer_length (srb);
+	}
 
 	/* verify that this amount is legal */
 	if (length > srb->request_bufflen) {
diff -urN linux-2.5.2-pre7/drivers/usb/storage/jumpshot.c linux/drivers/usb/storage/jumpshot.c
--- linux-2.5.2-pre7/drivers/usb/storage/jumpshot.c	Fri Sep 14 14:04:07 2001
+++ linux/drivers/usb/storage/jumpshot.c	Fri Jan  4 19:01:22 2002
@@ -284,7 +284,7 @@
 
                 if (use_sg) {
                         sg = (struct scatterlist *) dest;
-                        buffer = kmalloc(len, GFP_KERNEL);
+                        buffer = kmalloc(len, GFP_NOIO);
                         if (buffer == NULL)
                                 return USB_STOR_TRANSPORT_ERROR;
                         ptr = buffer;
@@ -399,7 +399,7 @@
 
                 if (use_sg) {
                         sg = (struct scatterlist *) src;
-                        buffer = kmalloc(len, GFP_KERNEL);
+                        buffer = kmalloc(len, GFP_NOIO);
                         if (buffer == NULL)
                                 return USB_STOR_TRANSPORT_ERROR;
                         ptr = buffer;
@@ -665,7 +665,7 @@
 
 
 	if (!us->extra) {
-		us->extra = kmalloc(sizeof(struct jumpshot_info), GFP_KERNEL);
+		us->extra = kmalloc(sizeof(struct jumpshot_info), GFP_NOIO);
 		if (!us->extra) {
 			US_DEBUGP("jumpshot_transport:  Gah! Can't allocate storage for jumpshot info struct!\n");
 			return USB_STOR_TRANSPORT_ERROR;
diff -urN linux-2.5.2-pre7/drivers/usb/storage/sddr09.c linux/drivers/usb/storage/sddr09.c
--- linux-2.5.2-pre7/drivers/usb/storage/sddr09.c	Fri Nov  9 14:37:14 2001
+++ linux/drivers/usb/storage/sddr09.c	Fri Jan  4 19:01:22 2002
@@ -1,6 +1,6 @@
 /* Driver for SanDisk SDDR-09 SmartMedia reader
  *
- * $Id: sddr09.c,v 1.21 2001/11/06 03:18:36 mdharm Exp $
+ * $Id: sddr09.c,v 1.22 2001/12/08 23:32:48 mdharm Exp $
  *
  * SDDR09 driver v0.1:
  *
@@ -79,7 +79,7 @@
 	// copy the data into the buffer.
 /*
 	if (xfer_len > 0) {
-		buffer = kmalloc(xfer_len, GFP_KERNEL);
+		buffer = kmalloc(xfer_len, GFP_NOIO);
 		if (!(command[0] & USB_DIR_IN))
 			memcpy(buffer, xfer_data, xfer_len);
 	}
@@ -303,7 +303,7 @@
 
 	if (use_sg) {
 		sg = (struct scatterlist *)content;
-		buffer = kmalloc(len, GFP_KERNEL);
+		buffer = kmalloc(len, GFP_NOIO);
 		if (buffer == NULL)
 			return USB_STOR_TRANSPORT_ERROR;
 		ptr = buffer;
@@ -630,17 +630,17 @@
 
 	alloc_blocks = (alloc_len + (1<<17) - 1) >> 17;
 	sg = kmalloc(alloc_blocks*sizeof(struct scatterlist),
-		GFP_KERNEL);
+		GFP_NOIO);
 	if (sg == NULL)
 		return 0;
 
 	for (i=0; i<alloc_blocks; i++) {
 		if (i<alloc_blocks-1) {
-			sg[i].address = kmalloc( (1<<17), GFP_KERNEL );
+			sg[i].address = kmalloc( (1<<17), GFP_NOIO );
 			sg[i].page = NULL;
 			sg[i].length = (1<<17);
 		} else {
-			sg[i].address = kmalloc(alloc_len, GFP_KERNEL);
+			sg[i].address = kmalloc(alloc_len, GFP_NOIO);
 			sg[i].page = NULL;
 			sg[i].length = alloc_len;
 		}
@@ -672,8 +672,8 @@
 		kfree(info->lba_to_pba);
 	if (info->pba_to_lba)
 		kfree(info->pba_to_lba);
-	info->lba_to_pba = kmalloc(numblocks*sizeof(int), GFP_KERNEL);
-	info->pba_to_lba = kmalloc(numblocks*sizeof(int), GFP_KERNEL);
+	info->lba_to_pba = kmalloc(numblocks*sizeof(int), GFP_NOIO);
+	info->pba_to_lba = kmalloc(numblocks*sizeof(int), GFP_NOIO);
 
 	if (info->lba_to_pba == NULL || info->pba_to_lba == NULL) {
 		if (info->lba_to_pba != NULL)
@@ -842,7 +842,7 @@
 
 	if (!us->extra) {
 		us->extra = kmalloc(
-			sizeof(struct sddr09_card_info), GFP_KERNEL);
+			sizeof(struct sddr09_card_info), GFP_NOIO);
 		if (!us->extra)
 			return USB_STOR_TRANSPORT_ERROR;
 		memset(us->extra, 0, sizeof(struct sddr09_card_info));
diff -urN linux-2.5.2-pre7/drivers/usb/storage/shuttle_usbat.c linux/drivers/usb/storage/shuttle_usbat.c
--- linux-2.5.2-pre7/drivers/usb/storage/shuttle_usbat.c	Sun Jul 29 21:11:50 2001
+++ linux/drivers/usb/storage/shuttle_usbat.c	Fri Jan  4 19:01:22 2002
@@ -1,6 +1,6 @@
 /* Driver for SCM Microsystems USB-ATAPI cable
  *
- * $Id: shuttle_usbat.c,v 1.14 2001/03/28 01:02:06 groovyjava Exp $
+ * $Id: shuttle_usbat.c,v 1.15 2001/12/08 23:32:48 mdharm Exp $
  *
  * Current development and maintenance by:
  *   (c) 2000, 2001 Robert Baruch (autophile@starband.net)
@@ -681,7 +681,7 @@
 
 	len = (65535/srb->transfersize) * srb->transfersize;
 	US_DEBUGP("Max read is %d bytes\n", len);
-	buffer = kmalloc(len, GFP_KERNEL);
+	buffer = kmalloc(len, GFP_NOIO);
 	if (buffer == NULL) // bloody hell!
 		return USB_STOR_TRANSPORT_FAILED;
 	sector = short_pack(data[7+3], data[7+2]);
diff -urN linux-2.5.2-pre7/drivers/usb/storage/transport.c linux/drivers/usb/storage/transport.c
--- linux-2.5.2-pre7/drivers/usb/storage/transport.c	Fri Nov  9 14:37:14 2001
+++ linux/drivers/usb/storage/transport.c	Fri Jan  4 19:01:22 2002
@@ -1,6 +1,6 @@
 /* Driver for USB Mass Storage compliant devices
  *
- * $Id: transport.c,v 1.41 2001/10/15 07:02:32 mdharm Exp $
+ * $Id: transport.c,v 1.42 2001/12/08 23:32:48 mdharm Exp $
  *
  * Current development and maintenance by:
  *   (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
@@ -388,7 +388,7 @@
 	devrequest *dr;
 
 	/* allocate the device request structure */
-	dr = kmalloc(sizeof(devrequest), GFP_KERNEL);
+	dr = kmalloc(sizeof(devrequest), GFP_NOIO);
 	if (!dr)
 		return -ENOMEM;
 
diff -urN linux-2.5.2-pre7/drivers/usb/storage/unusual_devs.h linux/drivers/usb/storage/unusual_devs.h
--- linux-2.5.2-pre7/drivers/usb/storage/unusual_devs.h	Sun Dec 16 15:46:59 2001
+++ linux/drivers/usb/storage/unusual_devs.h	Fri Jan  4 19:01:22 2002
@@ -1,7 +1,7 @@
 /* Driver for USB Mass Storage compliant devices
  * Ununsual Devices File
  *
- * $Id: unusual_devs.h,v 1.20 2001/09/02 05:12:57 mdharm Exp $
+ * $Id: unusual_devs.h,v 1.24 2001/12/29 03:12:45 mdharm Exp $
  *
  * Current development and maintenance by:
  *   (c) 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
@@ -58,6 +58,11 @@
 		"HP",
 		"CD-Writer+ 8200e",
 		US_SC_8070, US_PR_SCM_ATAPI, init_8200e, 0), 
+
+UNUSUAL_DEV(  0x03f0, 0x0307, 0x0001, 0x0001, 
+		"HP",
+		"CD-Writer+ CD-4e",
+		US_SC_8070, US_PR_SCM_ATAPI, init_8200e, 0), 
 #endif
 
 #ifdef CONFIG_USB_STORAGE_DPCM
@@ -86,6 +91,25 @@
 		"FinePix 1400Zoom",
 		US_SC_8070, US_PR_CBI, NULL, US_FL_FIX_INQUIRY),
 
+/* Reported by Peter Wächtler <pwaechtler@loewe-komp.de>
+ * The device needs the flags only.
+ */
+UNUSUAL_DEV(  0x04ce, 0x0002, 0x0074, 0x0074,
+		"ScanLogic",
+		"SL11R-IDE",
+		US_SC_SCSI, US_PR_BULK, NULL,
+		US_FL_FIX_INQUIRY),
+
+/* Reported by Kriston Fincher <kriston@airmail.net>
+ * Patch submitted by Sean Millichamp <sean@bruenor.org>
+ * This is to support the Panasonic PalmCam PV-SD4090
+ * This entry is needed because the device reports Sub=ff 
+ */
+UNUSUAL_DEV(  0x04da, 0x0901, 0x0100, 0x0200,
+               "Panasonic",
+               "LS-120 Camera",
+               US_SC_UFI, US_PR_CBI, NULL, 0),
+
 /* Most of the following entries were developed with the help of
  * Shuttle/SCM directly.
  */
@@ -161,14 +185,24 @@
 		US_SC_SCSI, US_PR_BULK, usb_stor_euscsi_init,
 		US_FL_SCM_MULT_TARG ),
 
+/* Iomega Clik! Drive 
+ * Reported by David Chatenay <dchatenay@hotmail.com>
+ * The reason this is needed is not fully known.
+ */
+UNUSUAL_DEV(  0x0525, 0xa140, 0x0100, 0x0100,
+		"Iomega",
+		"USB Clik! 40",
+		US_SC_8070, US_PR_BULK, NULL,
+		US_FL_FIX_INQUIRY | US_FL_START_STOP ),
+
 /* This entry is needed because the device reports Sub=ff */
-UNUSUAL_DEV(  0x054c, 0x0010, 0x0106, 0x0322, 
+UNUSUAL_DEV(  0x054c, 0x0010, 0x0106, 0x0422, 
 		"Sony",
 		"DSC-S30/S70/S75/505V/F505", 
 		US_SC_SCSI, US_PR_CB, NULL,
 		US_FL_SINGLE_LUN | US_FL_START_STOP | US_FL_MODE_XLATE ),
 
-/* Reported by win@geeks.nl */
+/* Reported by wim@geeks.nl */
 UNUSUAL_DEV(  0x054c, 0x0025, 0x0100, 0x0100, 
 		"Sony",
 		"Memorystick NW-MS7",
@@ -194,6 +228,13 @@
 		US_SC_UFI, US_PR_CB, NULL,
 		US_FL_SINGLE_LUN | US_FL_START_STOP ),
 		
+/* Submitted by Nathan Babb <nathan@lexi.com> */
+UNUSUAL_DEV(  0x054c, 0x006d, 0x0000, 0x9999,
+                "Sony",
+		"PEG Mass Storage",
+		US_SC_8070, US_PR_CBI, NULL,
+		US_FL_FIX_INQUIRY ),
+		
 UNUSUAL_DEV(  0x057b, 0x0000, 0x0000, 0x0299, 
 		"Y-E Data",
 		"Flashbuster-U",
@@ -264,6 +305,14 @@
 		US_FL_SINGLE_LUN | US_FL_START_STOP ),
 #endif
 
+/* Submitted by f.brugmans@hccnet.nl
+ * Needed for START_STOP flag */
+UNUSUAL_DEV( 0x0686, 0x4007, 0x0001, 0x0001,
+                "Minolta",
+                "Dimage S304",
+                US_SC_SCSI, US_PR_BULK, NULL,
+                US_FL_START_STOP ),
+
 UNUSUAL_DEV(  0x0693, 0x0002, 0x0100, 0x0100, 
 		"Hagiwara",
 		"FlashGate SmartMedia",
@@ -307,7 +356,7 @@
                 US_SC_QIC, US_PR_FREECOM, freecom_init, 0),
 #endif
 
-UNUSUAL_DEV(  0x07af, 0x0004, 0x0100, 0x0100, 
+UNUSUAL_DEV(  0x07af, 0x0004, 0x0100, 0x0133, 
 		"Microtech",
 		"USB-SCSI-DB25",
 		US_SC_SCSI, US_PR_BULK, usb_stor_euscsi_init,
@@ -374,16 +423,23 @@
 		"Simple Tech/Datafab CF+SM Reader",
 		US_SC_SCSI, US_PR_DATAFAB, NULL,
 		US_FL_MODE_XLATE | US_FL_START_STOP ),
+
+/* Submitted by Olaf Hering <olh@suse.de> */
+UNUSUAL_DEV(  0x07c4, 0xa109, 0x0000, 0xffff,
+		"Datafab Systems, Inc.",
+		"USB to CF + SM Combo (LC1)",
+		US_SC_SCSI, US_PR_DATAFAB, NULL,
+		US_FL_MODE_XLATE | US_FL_START_STOP ),
 #endif
 
-/* Casio QV 2x00/3x00/8000 digital still cameras are not conformant
+/* Casio QV 2x00/3x00/4000/8000 digital still cameras are not conformant
  * to the USB storage specification in two ways:
  * - They tell us they are using transport protocol CBI. In reality they
  *   are using transport protocol CB.
  * - They don't like the INQUIRY command. So we must handle this command
  *   of the SCSI layer ourselves.
  */
-UNUSUAL_DEV( 0x07cf, 0x1001, 0x9009, 0x9009,
+UNUSUAL_DEV( 0x07cf, 0x1001, 0x1000, 0x9009,
                 "Casio",
                 "QV DigitalCamera",
                 US_SC_8070, US_PR_CB, NULL,
@@ -402,3 +458,12 @@
                 US_SC_ISD200, US_PR_BULK, isd200_Initialization,
                 0 ),
 #endif
+
+/* Reported by Dan Pilone <pilone@slac.com>
+ * The device needs the flags only.
+ */
+UNUSUAL_DEV(  0x1065, 0x2136, 0x0000, 0x9999,
+		"CCYU TECHNOLOGY",
+		"EasyDisk Portable Device",
+		US_SC_SCSI, US_PR_BULK, NULL,
+		US_FL_MODE_XLATE | US_FL_START_STOP),
diff -urN linux-2.5.2-pre7/drivers/usb/usb.c linux/drivers/usb/usb.c
--- linux-2.5.2-pre7/drivers/usb/usb.c	Fri Jan  4 19:01:17 2002
+++ linux/drivers/usb/usb.c	Fri Jan  4 19:01:22 2002
@@ -150,9 +150,13 @@
 		struct usb_interface *interface = &dev->actconfig->interface[i];
 		
 		if (interface->driver == driver) {
+			if (driver->owner)
+				__MOD_INC_USE_COUNT(driver->owner);
 			down(&driver->serialize);
 			driver->disconnect(dev, interface->private_data);
 			up(&driver->serialize);
+			if (driver->owner)
+				__MOD_DEC_USE_COUNT(driver->owner);
 			/* if driver->disconnect didn't release the interface */
 			if (interface->driver)
 				usb_driver_release_interface(driver, interface);
@@ -781,6 +785,8 @@
 		driver = list_entry(tmp, struct usb_driver, driver_list);
 		tmp = tmp->next;
 
+		if (driver->owner)
+			__MOD_INC_USE_COUNT(driver->owner);
 		id = driver->id_table;
 		/* new style driver? */
 		if (id) {
@@ -804,6 +810,8 @@
 			private = driver->probe(dev, ifnum, NULL);
 			up(&driver->serialize);
 		}
+		if (driver->owner)
+			__MOD_DEC_USE_COUNT(driver->owner);
 
 		/* probe() may have changed the config on us */
 		interface = dev->actconfig->interface + ifnum;
@@ -1887,9 +1895,13 @@
 			struct usb_interface *interface = &dev->actconfig->interface[i];
 			struct usb_driver *driver = interface->driver;
 			if (driver) {
+				if (driver->owner)
+					__MOD_INC_USE_COUNT(driver->owner);
 				down(&driver->serialize);
 				driver->disconnect(dev, interface->private_data);
 				up(&driver->serialize);
+				if (driver->owner)
+					__MOD_DEC_USE_COUNT(driver->owner);
 				/* if driver->disconnect didn't release the interface */
 				if (interface->driver)
 					usb_driver_release_interface(driver, interface);
diff -urN linux-2.5.2-pre7/drivers/usb/usbnet.c linux/drivers/usb/usbnet.c
--- linux-2.5.2-pre7/drivers/usb/usbnet.c	Fri Jan  4 19:01:17 2002
+++ linux/drivers/usb/usbnet.c	Fri Jan  4 19:01:22 2002
@@ -1571,7 +1571,7 @@
 	struct urb		*urb = 0;
 	struct skb_data		*entry;
 	struct driver_info	*info = dev->driver_info;
-	int			flags;
+	unsigned long		flags;
 #ifdef	CONFIG_USB_NET1080
 	struct nc_header	*header = 0;
 	struct nc_trailer	*trailer = 0;
diff -urN linux-2.5.2-pre7/fs/adfs/super.c linux/fs/adfs/super.c
--- linux-2.5.2-pre7/fs/adfs/super.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/adfs/super.c	Fri Jan  4 19:01:22 2002
@@ -39,7 +39,7 @@
 	va_end(args);
 
 	printk(KERN_CRIT "ADFS-fs error (device %s)%s%s: %s\n",
-		bdevname(sb->s_dev), function ? ": " : "",
+		sb->s_id, function ? ": " : "",
 		function ? function : "", error_buf);
 }
 
@@ -308,7 +308,6 @@
 	struct buffer_head *bh;
 	struct object_info root_obj;
 	unsigned char *b_data;
-	kdev_t dev = sb->s_dev;
 
 	/* set default options */
 	sb->u.adfs_sb.s_uid = 0;
@@ -330,7 +329,7 @@
 	if (adfs_checkbblk(b_data)) {
 		if (!silent)
 			printk("VFS: Can't find an adfs filesystem on dev "
-				"%s.\n", bdevname(dev));
+				"%s.\n", sb->s_id);
 		goto error_free_bh;
 	}
 
@@ -342,7 +341,7 @@
 	if (adfs_checkdiscrecord(dr)) {
 		if (!silent)
 			printk("VPS: Can't find an adfs filesystem on dev "
-				"%s.\n", bdevname(dev));
+				"%s.\n", sb->s_id);
 		goto error_free_bh;
 	}
 
@@ -363,7 +362,7 @@
 	} else {
 		if (!silent)
 			printk(KERN_ERR "VFS: Unsupported blocksize on dev "
-				"%s.\n", bdevname(dev));
+				"%s.\n", sb->s_id);
 		goto error;
 	}
 
diff -urN linux-2.5.2-pre7/fs/affs/amigaffs.c linux/fs/affs/amigaffs.c
--- linux-2.5.2-pre7/fs/affs/amigaffs.c	Tue Sep 11 08:19:35 2001
+++ linux/fs/affs/amigaffs.c	Fri Jan  4 19:01:22 2002
@@ -453,7 +453,7 @@
 	vsprintf(ErrorBuffer,fmt,args);
 	va_end(args);
 
-	printk(KERN_CRIT "AFFS error (device %s): %s(): %s\n", bdevname(sb->s_dev),
+	printk(KERN_CRIT "AFFS error (device %s): %s(): %s\n", sb->s_id,
 		function,ErrorBuffer);
 	if (!(sb->s_flags & MS_RDONLY))
 		printk(KERN_WARNING "AFFS: Remounting filesystem read-only\n");
@@ -470,7 +470,7 @@
 	vsprintf(ErrorBuffer,fmt,args);
 	va_end(args);
 
-	printk(KERN_WARNING "AFFS warning (device %s): %s(): %s\n", bdevname(sb->s_dev),
+	printk(KERN_WARNING "AFFS warning (device %s): %s(): %s\n", sb->s_id,
 		function,ErrorBuffer);
 }
 
diff -urN linux-2.5.2-pre7/fs/affs/bitmap.c linux/fs/affs/bitmap.c
--- linux-2.5.2-pre7/fs/affs/bitmap.c	Wed Apr 25 14:57:09 2001
+++ linux/fs/affs/bitmap.c	Fri Jan  4 19:01:22 2002
@@ -282,7 +282,7 @@
 
 	if (!AFFS_ROOT_TAIL(sb, AFFS_SB->s_root_bh)->bm_flag) {
 		printk(KERN_NOTICE "AFFS: Bitmap invalid - mounting %s read only\n",
-			kdevname(sb->s_dev));
+			sb->s_id);
 		sb->s_flags |= MS_RDONLY;
 		return 0;
 	}
@@ -316,7 +316,7 @@
 		}
 		if (affs_checksum_block(sb, bh)) {
 			printk(KERN_WARNING "AFFS: Bitmap %u invalid - mounting %s read only.\n",
-			       bm->bm_key, kdevname(sb->s_dev));
+			       bm->bm_key, sb->s_id);
 			sb->s_flags |= MS_RDONLY;
 			goto out;
 		}
diff -urN linux-2.5.2-pre7/fs/affs/super.c linux/fs/affs/super.c
--- linux-2.5.2-pre7/fs/affs/super.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/affs/super.c	Fri Jan  4 19:01:22 2002
@@ -300,7 +300,7 @@
 		for (num_bm = 0; num_bm < 2; num_bm++) {
 			pr_debug("AFFS: Dev %s, trying root=%u, bs=%d, "
 				"size=%d, reserved=%d\n",
-				kdevname(dev),
+				sb->s_id,
 				AFFS_SB->s_root_block + num_bm,
 				blocksize, size, reserved);
 			root_bh = affs_bread(sb, AFFS_SB->s_root_block + num_bm);
@@ -320,7 +320,7 @@
 	}
 	if (!silent)
 		printk(KERN_ERR "AFFS: No valid root block on device %s\n",
-			kdevname(dev));
+			sb->s_id);
 	goto out_error;
 
 	/* N.B. after this point bh must be released */
@@ -343,7 +343,7 @@
 	if ((chksum == FS_DCFFS || chksum == MUFS_DCFFS || chksum == FS_DCOFS
 	     || chksum == MUFS_DCOFS) && !(sb->s_flags & MS_RDONLY)) {
 		printk(KERN_NOTICE "AFFS: Dircache FS - mounting %s read only\n",
-			kdevname(dev));
+			sb->s_id);
 		sb->s_flags |= MS_RDONLY;
 		AFFS_SB->s_flags |= SF_READONLY;
 	}
@@ -379,7 +379,7 @@
 			break;
 		default:
 			printk(KERN_ERR "AFFS: Unknown filesystem on device %s: %08X\n",
-				kdevname(dev), chksum);
+				sb->s_id, chksum);
 			goto out_error;
 	}
 
diff -urN linux-2.5.2-pre7/fs/bfs/dir.c linux/fs/bfs/dir.c
--- linux-2.5.2-pre7/fs/bfs/dir.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/bfs/dir.c	Fri Jan  4 19:01:22 2002
@@ -33,7 +33,7 @@
 
 	if (f->f_pos & (BFS_DIRENT_SIZE-1)) {
 		printf("Bad f_pos=%08lx for %s:%08lx\n", (unsigned long)f->f_pos, 
-			bdevname(dir->i_dev), dir->i_ino);
+			dir->i_sb->s_id, dir->i_ino);
 		return -EBADF;
 	}
 
@@ -168,7 +168,7 @@
 		goto out_brelse;
 
 	if (!inode->i_nlink) {
-		printf("unlinking non-existent file %s:%lu (nlink=%d)\n", bdevname(inode->i_dev), 
+		printf("unlinking non-existent file %s:%lu (nlink=%d)\n", inode->i_sb->s_id, 
 				inode->i_ino, inode->i_nlink);
 		inode->i_nlink = 1;
 	}
diff -urN linux-2.5.2-pre7/fs/bfs/inode.c linux/fs/bfs/inode.c
--- linux-2.5.2-pre7/fs/bfs/inode.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/bfs/inode.c	Fri Jan  4 19:01:22 2002
@@ -40,7 +40,7 @@
 	int block, off;
 
 	if (ino < BFS_ROOT_INO || ino > inode->i_sb->su_lasti) {
-		printf("Bad inode number %s:%08lx\n", bdevname(inode->i_dev), ino);
+		printf("Bad inode number %s:%08lx\n", inode->i_sb->s_id, ino);
 		make_bad_inode(inode);
 		return;
 	}
@@ -48,7 +48,7 @@
 	block = (ino - BFS_ROOT_INO)/BFS_INODES_PER_BLOCK + 1;
 	bh = sb_bread(inode->i_sb, block);
 	if (!bh) {
-		printf("Unable to read inode %s:%08lx\n", bdevname(inode->i_dev), ino);
+		printf("Unable to read inode %s:%08lx\n", inode->i_sb->s_id, ino);
 		make_bad_inode(inode);
 		return;
 	}
@@ -92,7 +92,7 @@
 	int block, off;
 
 	if (ino < BFS_ROOT_INO || ino > inode->i_sb->su_lasti) {
-		printf("Bad inode number %s:%08lx\n", bdevname(inode->i_dev), ino);
+		printf("Bad inode number %s:%08lx\n", inode->i_sb->s_id, ino);
 		return;
 	}
 
@@ -100,7 +100,7 @@
 	block = (ino - BFS_ROOT_INO)/BFS_INODES_PER_BLOCK + 1;
 	bh = sb_bread(inode->i_sb, block);
 	if (!bh) {
-		printf("Unable to read inode %s:%08lx\n", bdevname(inode->i_dev), ino);
+		printf("Unable to read inode %s:%08lx\n", inode->i_sb->s_id, ino);
 		unlock_kernel();
 		return;
 	}
@@ -152,7 +152,7 @@
 	block = (ino - BFS_ROOT_INO)/BFS_INODES_PER_BLOCK + 1;
 	bh = sb_bread(s, block);
 	if (!bh) {
-		printf("Unable to read inode %s:%08lx\n", bdevname(inode->i_dev), ino);
+		printf("Unable to read inode %s:%08lx\n", inode->i_sb->s_id, ino);
 		unlock_kernel();
 		return;
 	}
@@ -252,11 +252,11 @@
 	if (bfs_sb->s_magic != BFS_MAGIC) {
 		if (!silent)
 			printf("No BFS filesystem on %s (magic=%08x)\n", 
-				bdevname(s->s_dev), bfs_sb->s_magic);
+				s->s_id, bfs_sb->s_magic);
 		goto out;
 	}
 	if (BFS_UNCLEAN(bfs_sb, s) && !silent)
-		printf("%s is unclean, continuing\n", bdevname(s->s_dev));
+		printf("%s is unclean, continuing\n", s->s_id);
 
 	s->s_magic = BFS_MAGIC;
 	s->su_bfs_sb = bfs_sb;
diff -urN linux-2.5.2-pre7/fs/coda/psdev.c linux/fs/coda/psdev.c
--- linux-2.5.2-pre7/fs/coda/psdev.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/coda/psdev.c	Fri Jan  4 19:01:22 2002
@@ -293,7 +293,7 @@
 	int idx;
 
 	lock_kernel();
-	idx = MINOR(inode->i_rdev);
+	idx = minor(inode->i_rdev);
 	if(idx >= MAX_CODADEVS) {
 		unlock_kernel();
 		return -ENODEV;
diff -urN linux-2.5.2-pre7/fs/devfs/base.c linux/fs/devfs/base.c
--- linux-2.5.2-pre7/fs/devfs/base.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/devfs/base.c	Fri Jan  4 19:01:22 2002
@@ -894,7 +894,7 @@
     {
 	devfs_dealloc_devnum ( S_ISCHR (de->mode) ? DEVFS_SPECIAL_CHR :
 			       DEVFS_SPECIAL_BLK,
-			       MKDEV (de->u.fcb.u.device.major,
+			       mk_kdev(de->u.fcb.u.device.major,
 				      de->u.fcb.u.device.minor) );
     }
     WRITE_ENTRY_MAGIC (de, 0);
@@ -1552,7 +1552,7 @@
     if ( ( S_ISCHR (mode) || S_ISBLK (mode) ) &&
 	 (flags & DEVFS_FL_AUTO_DEVNUM) )
     {
-	if ( ( devnum = devfs_alloc_devnum (devtype) ) == NODEV )
+	if ( kdev_none( devnum = devfs_alloc_devnum (devtype) ) )
 	{
 	    PRINTK ("(%s): exhausted %s device numbers\n",
 		    name, S_ISCHR (mode) ? "char" : "block");
@@ -1564,14 +1564,14 @@
     if ( ( de = _devfs_prepare_leaf (&dir, name, mode) ) == NULL )
     {
 	PRINTK ("(%s): could not prepare leaf\n", name);
-	if (devnum != NODEV) devfs_dealloc_devnum (devtype, devnum);
+	if (!kdev_none(devnum)) devfs_dealloc_devnum (devtype, devnum);
 	return NULL;
     }
     if ( S_ISCHR (mode) || S_ISBLK (mode) )
     {
 	de->u.fcb.u.device.major = major;
 	de->u.fcb.u.device.minor = minor;
-	de->u.fcb.autogen = (devnum == NODEV) ? FALSE : TRUE;
+	de->u.fcb.autogen = kdev_none(devnum) ? FALSE : TRUE;
     }
     else if ( !S_ISREG (mode) )
     {
@@ -1601,7 +1601,7 @@
     {
 	PRINTK ("(%s): could not append to parent, err: %d\n", name, err);
 	devfs_put (dir);
-	if (devnum != NODEV) devfs_dealloc_devnum (devtype, devnum);
+	if (!kdev_none(devnum)) devfs_dealloc_devnum (devtype, devnum);
 	return NULL;
     }
     DPRINTK (DEBUG_REGISTER, "(%s): de: %p dir: %p \"%s\"  pp: %p\n",
@@ -2413,7 +2413,7 @@
 {
     int tmp;
     int retval = 0;
-    kdev_t dev = MKDEV (de->u.fcb.u.device.major, de->u.fcb.u.device.minor);
+    kdev_t dev = mk_kdev(de->u.fcb.u.device.major, de->u.fcb.u.device.minor);
     struct block_device_operations *bdops;
     extern int warn_no_part;
 
@@ -2599,14 +2599,14 @@
     inode->i_rdev = NODEV;
     if ( S_ISCHR (de->mode) )
     {
-	inode->i_rdev = MKDEV (de->u.fcb.u.device.major,
+	inode->i_rdev = mk_kdev(de->u.fcb.u.device.major,
 			       de->u.fcb.u.device.minor);
 	inode->i_cdev = cdget ( kdev_t_to_nr (inode->i_rdev) );
 	is_fcb = TRUE;
     }
     else if ( S_ISBLK (de->mode) )
     {
-	inode->i_rdev = MKDEV (de->u.fcb.u.device.major,
+	inode->i_rdev = mk_kdev(de->u.fcb.u.device.major,
 			       de->u.fcb.u.device.minor);
 	if (bd_acquire (inode) == 0)
 	{
diff -urN linux-2.5.2-pre7/fs/devfs/util.c linux/fs/devfs/util.c
--- linux-2.5.2-pre7/fs/devfs/util.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/devfs/util.c	Fri Jan  4 19:01:22 2002
@@ -267,7 +267,7 @@
 	if (minor >= 256) continue;
 	__set_bit (minor, entry->bits);
 	up (semaphore);
-	return MKDEV (entry->major, minor);
+	return mk_kdev(entry->major, minor);
     }
     /*  Need to allocate a new major  */
     if ( ( entry = kmalloc (sizeof *entry, GFP_KERNEL) ) == NULL )
@@ -289,7 +289,7 @@
     else list->last->next = entry;
     list->last = entry;
     up (semaphore);
-    return MKDEV (entry->major, 0);
+    return mk_kdev(entry->major, 0);
 }   /*  End Function devfs_alloc_devnum  */
 EXPORT_SYMBOL(devfs_alloc_devnum);
 
@@ -309,7 +309,7 @@
     struct device_list *list;
     struct minor_list *entry;
 
-    if (devnum == NODEV) return;
+    if (kdev_none(devnum)) return;
     if (type == DEVFS_SPECIAL_CHR)
     {
 	semaphore = &char_semaphore;
diff -urN linux-2.5.2-pre7/fs/ext2/inode.c linux/fs/ext2/inode.c
--- linux-2.5.2-pre7/fs/ext2/inode.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/ext2/inode.c	Fri Jan  4 19:01:22 2002
@@ -1135,9 +1135,8 @@
 		ll_rw_block (WRITE, 1, &bh);
 		wait_on_buffer (bh);
 		if (buffer_req(bh) && !buffer_uptodate(bh)) {
-			printk ("IO error syncing ext2 inode ["
-				"%s:%08lx]\n",
-				bdevname(inode->i_dev), inode->i_ino);
+			printk ("IO error syncing ext2 inode [%s:%08lx]\n",
+				inode->i_sb->s_id, inode->i_ino);
 			err = -EIO;
 		}
 	}
diff -urN linux-2.5.2-pre7/fs/ext2/super.c linux/fs/ext2/super.c
--- linux-2.5.2-pre7/fs/ext2/super.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/ext2/super.c	Fri Jan  4 19:01:22 2002
@@ -53,9 +53,9 @@
 	    (le16_to_cpu(sb->u.ext2_sb.s_es->s_errors) == EXT2_ERRORS_PANIC &&
 	     !test_opt (sb, ERRORS_CONT) && !test_opt (sb, ERRORS_RO)))
 		panic ("EXT2-fs panic (device %s): %s: %s\n",
-		       bdevname(sb->s_dev), function, error_buf);
+		       sb->s_id, function, error_buf);
 	printk (KERN_CRIT "EXT2-fs error (device %s): %s: %s\n",
-		bdevname(sb->s_dev), function, error_buf);
+		sb->s_id, function, error_buf);
 	if (test_opt (sb, ERRORS_RO) ||
 	    (le16_to_cpu(sb->u.ext2_sb.s_es->s_errors) == EXT2_ERRORS_RO &&
 	     !test_opt (sb, ERRORS_CONT) && !test_opt (sb, ERRORS_PANIC))) {
@@ -81,7 +81,7 @@
 	va_end (args);
 	sb->s_flags |= MS_RDONLY;
 	panic ("EXT2-fs panic (device %s): %s: %s\n",
-	       bdevname(sb->s_dev), function, error_buf);
+	       sb->s_id, function, error_buf);
 }
 
 void ext2_warning (struct super_block * sb, const char * function,
@@ -93,7 +93,7 @@
 	vsprintf (error_buf, fmt, args);
 	va_end (args);
 	printk (KERN_WARNING "EXT2-fs warning (device %s): %s: %s\n",
-		bdevname(sb->s_dev), function, error_buf);
+		sb->s_id, function, error_buf);
 }
 
 void ext2_update_dynamic_rev(struct super_block *sb)
@@ -408,7 +408,6 @@
 	unsigned short resgid = EXT2_DEF_RESGID;
 	unsigned long logic_sb_block = 1;
 	unsigned long offset = 0;
-	kdev_t dev = sb->s_dev;
 	int blocksize = BLOCK_SIZE;
 	int db_count;
 	int i, j;
@@ -457,7 +456,7 @@
 	if (sb->s_magic != EXT2_SUPER_MAGIC) {
 		if (!silent)
 			printk ("VFS: Can't find ext2 filesystem on dev %s.\n",
-				bdevname(dev));
+				sb->s_id);
 		goto failed_mount;
 	}
 	if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV &&
@@ -474,14 +473,14 @@
 	if ((i = EXT2_HAS_INCOMPAT_FEATURE(sb, ~EXT2_FEATURE_INCOMPAT_SUPP))) {
 		printk("EXT2-fs: %s: couldn't mount because of "
 		       "unsupported optional features (%x).\n",
-		       bdevname(dev), i);
+		       sb->s_id, i);
 		goto failed_mount;
 	}
 	if (!(sb->s_flags & MS_RDONLY) &&
 	    (i = EXT2_HAS_RO_COMPAT_FEATURE(sb, ~EXT2_FEATURE_RO_COMPAT_SUPP))){
 		printk("EXT2-fs: %s: couldn't mount RDWR because of "
 		       "unsupported optional features (%x).\n",
-		       bdevname(dev), i);
+		       sb->s_id, i);
 		goto failed_mount;
 	}
 	blocksize = BLOCK_SIZE << le32_to_cpu(EXT2_SB(sb)->s_es->s_log_block_size);
@@ -558,13 +557,13 @@
 		if (!silent)
 			printk ("VFS: Can't find an ext2 filesystem on dev "
 				"%s.\n",
-				bdevname(dev));
+				sb->s_id);
 		goto failed_mount;
 	}
 	if (sb->s_blocksize != bh->b_size) {
 		if (!silent)
 			printk ("VFS: Unsupported blocksize on dev "
-				"%s.\n", bdevname(dev));
+				"%s.\n", sb->s_id);
 		goto failed_mount;
 	}
 
@@ -737,7 +736,7 @@
 					       ~EXT2_FEATURE_RO_COMPAT_SUPP))) {
 			printk("EXT2-fs: %s: couldn't remount RDWR because of "
 			       "unsupported optional features (%x).\n",
-			       bdevname(sb->s_dev), ret);
+			       sb->s_id, ret);
 			return -EROFS;
 		}
 		/*
diff -urN linux-2.5.2-pre7/fs/ext3/ialloc.c linux/fs/ext3/ialloc.c
--- linux-2.5.2-pre7/fs/ext3/ialloc.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/ext3/ialloc.c	Fri Jan  4 19:01:22 2002
@@ -189,10 +189,6 @@
 	struct ext3_super_block * es;
 	int fatal = 0, err;
 
-	if (kdev_none(inode->i_dev)) {
-		printk ("ext3_free_inode: inode has no device\n");
-		return;
-	}
 	if (atomic_read(&inode->i_count) > 1) {
 		printk ("ext3_free_inode: inode has count=%d\n",
 					atomic_read(&inode->i_count));
diff -urN linux-2.5.2-pre7/fs/ext3/super.c linux/fs/ext3/super.c
--- linux-2.5.2-pre7/fs/ext3/super.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/ext3/super.c	Fri Jan  4 19:01:22 2002
@@ -163,7 +163,7 @@
 
 	if (ext3_error_behaviour(sb) == EXT3_ERRORS_PANIC) 
 		panic ("EXT3-fs (device %s): panic forced after error\n",
-		       bdevname(sb->s_dev));
+		       sb->s_id);
 
 	if (ext3_error_behaviour(sb) == EXT3_ERRORS_RO) {
 		printk (KERN_CRIT "Remounting filesystem read-only\n");
@@ -183,7 +183,7 @@
 	va_end (args);
 
 	printk (KERN_CRIT "EXT3-fs error (device %s): %s: %s\n",
-		bdevname(sb->s_dev), function, error_buf);
+		sb->s_id, function, error_buf);
 
 	ext3_handle_error(sb);
 }
@@ -231,7 +231,7 @@
 	const char *errstr = ext3_decode_error(sb, errno, nbuf);
 
 	printk (KERN_CRIT "EXT3-fs error (device %s) in %s: %s\n",
-		bdevname(sb->s_dev), function, errstr);
+		sb->s_id, function, errstr);
 	
 	ext3_handle_error(sb);
 }
@@ -259,10 +259,10 @@
 
 	if (ext3_error_behaviour(sb) == EXT3_ERRORS_PANIC)
 		panic ("EXT3-fs panic (device %s): %s: %s\n",
-		       bdevname(sb->s_dev), function, error_buf);
+		       sb->s_id, function, error_buf);
 
 	printk (KERN_CRIT "EXT3-fs abort (device %s): %s: %s\n",
-		bdevname(sb->s_dev), function, error_buf);
+		sb->s_id, function, error_buf);
 
 	if (sb->s_flags & MS_RDONLY)
 		return;
@@ -293,7 +293,7 @@
 	/* AKPM: is this sufficient? */
 	sb->s_flags |= MS_RDONLY;
 	panic ("EXT3-fs panic (device %s): %s: %s\n",
-	       bdevname(sb->s_dev), function, error_buf);
+	       sb->s_id, function, error_buf);
 }
 
 void ext3_warning (struct super_block * sb, const char * function,
@@ -305,7 +305,7 @@
 	vsprintf (error_buf, fmt, args);
 	va_end (args);
 	printk (KERN_WARNING "EXT3-fs warning (device %s): %s: %s\n",
-		bdevname(sb->s_dev), function, error_buf);
+		sb->s_id, function, error_buf);
 }
 
 void ext3_update_dynamic_rev(struct super_block *sb)
@@ -389,8 +389,8 @@
 	list_for_each(l, &sbi->s_orphan) {
 		struct inode *inode = orphan_list_entry(l);
 		printk(KERN_ERR "  "
-		       "inode 0x%04x.0x%04x:%ld at %p: mode %o, nlink %d, next %d\n",
-		       major(inode->i_dev), minor(inode->i_dev), inode->i_ino, inode,
+		       "inode %s:%ld at %p: mode %o, nlink %d, next %d\n",
+		       inode->i_sb->s_id, inode->i_ino, inode,
 		       inode->i_mode, inode->i_nlink, 
 		       le32_to_cpu(NEXT_ORPHAN(inode)));
 	}
@@ -712,7 +712,7 @@
 			EXT3_INODES_PER_GROUP(sb),
 			sbi->s_mount_opt);
 	printk(KERN_INFO "EXT3 FS " EXT3FS_VERSION ", " EXT3FS_DATE " on %s, ",
-				bdevname(sb->s_dev));
+				sb->s_id);
 	if (EXT3_SB(sb)->s_journal->j_inode == NULL) {
 		printk("external journal on %s\n",
 				bdevname(EXT3_SB(sb)->s_journal->j_dev));
@@ -813,7 +813,7 @@
 
 	if (s_flags & MS_RDONLY) {
 		printk(KERN_INFO "EXT3-fs: %s: orphan cleanup on readonly fs\n",
-		       bdevname(sb->s_dev));
+		       sb->s_id);
 		sb->s_flags &= ~MS_RDONLY;
 	}
 
@@ -859,10 +859,10 @@
 
 	if (nr_orphans)
 		printk(KERN_INFO "EXT3-fs: %s: %d orphan inode%s deleted\n",
-		       bdevname(sb->s_dev), PLURAL(nr_orphans));
+		       sb->s_id, PLURAL(nr_orphans));
 	if (nr_truncates)
 		printk(KERN_INFO "EXT3-fs: %s: %d truncate%s cleaned up\n",
-		       bdevname(sb->s_dev), PLURAL(nr_truncates));
+		       sb->s_id, PLURAL(nr_truncates));
 	sb->s_flags = s_flags; /* Restore MS_RDONLY status */
 }
 
@@ -916,10 +916,8 @@
 	sbi->s_mount_opt = 0;
 	sbi->s_resuid = EXT3_DEF_RESUID;
 	sbi->s_resgid = EXT3_DEF_RESGID;
-	if (!parse_options ((char *) data, &sb_block, sbi, &journal_inum, 0)) {
-		sb->s_dev = NODEV;
+	if (!parse_options ((char *) data, &sb_block, sbi, &journal_inum, 0))
 		goto out_fail;
-	}
 
 	blocksize = sb_min_blocksize(sb, EXT3_MIN_BLOCK_SIZE);
 
@@ -947,7 +945,7 @@
 		if (!silent)
 			printk(KERN_ERR 
 			       "VFS: Can't find ext3 filesystem on dev %s.\n",
-			       bdevname(dev));
+			       sb->s_id);
 		goto failed_mount;
 	}
 	if (le32_to_cpu(es->s_rev_level) == EXT3_GOOD_OLD_REV &&
@@ -965,14 +963,14 @@
 	if ((i = EXT3_HAS_INCOMPAT_FEATURE(sb, ~EXT3_FEATURE_INCOMPAT_SUPP))) {
 		printk(KERN_ERR "EXT3-fs: %s: couldn't mount because of "
 		       "unsupported optional features (%x).\n",
-		       bdevname(dev), i);
+		       sb->s_id, i);
 		goto failed_mount;
 	}
 	if (!(sb->s_flags & MS_RDONLY) &&
 	    (i = EXT3_HAS_RO_COMPAT_FEATURE(sb, ~EXT3_FEATURE_RO_COMPAT_SUPP))){
 		printk(KERN_ERR "EXT3-fs: %s: couldn't mount RDWR because of "
 		       "unsupported optional features (%x).\n",
-		       bdevname(dev), i);
+		       sb->s_id, i);
 		goto failed_mount;
 	}
 	blocksize = BLOCK_SIZE << le32_to_cpu(es->s_log_block_size);
@@ -981,7 +979,7 @@
 	    blocksize > EXT3_MAX_BLOCK_SIZE) {
 		printk(KERN_ERR 
 		       "EXT3-fs: Unsupported filesystem blocksize %d on %s.\n",
-		       blocksize, bdevname(dev));
+		       blocksize, sb->s_id);
 		goto failed_mount;
 	}
 
@@ -1135,7 +1133,7 @@
 		if (!silent)
 			printk (KERN_ERR
 				"ext3: No journal on filesystem on %s\n",
-				bdevname(dev));
+				sb->s_id);
 		goto failed_mount2;
 	}
 
@@ -1654,7 +1652,7 @@
 				printk(KERN_WARNING "EXT3-fs: %s: couldn't "
 				       "remount RDWR because of unsupported "
 				       "optional features (%x).\n",
-				       bdevname(sb->s_dev), ret);
+				       sb->s_id, ret);
 				return -EROFS;
 			}
 			/*
diff -urN linux-2.5.2-pre7/fs/fat/cache.c linux/fs/fat/cache.c
--- linux-2.5.2-pre7/fs/fat/cache.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/fat/cache.c	Fri Jan  4 19:01:22 2002
@@ -189,7 +189,7 @@
 
 	for (walk = fat_cache; walk; walk = walk->next) {
 		if (walk->sb)
-			printk("<%s,%d>(%d,%d) ", bdevname(walk->sb->s_dev),
+			printk("<%s,%d>(%d,%d) ", walk->sb->s_dev->s_id,
 			       walk->start_cluster, walk->file_cluster,
 			       walk->disk_cluster);
 		else printk("-- ");
diff -urN linux-2.5.2-pre7/fs/fat/inode.c linux/fs/fat/inode.c
--- linux-2.5.2-pre7/fs/fat/inode.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/fat/inode.c	Fri Jan  4 19:01:22 2002
@@ -799,7 +799,7 @@
 out_invalid:
 	if (!silent) {
 		printk("VFS: Can't find a valid FAT filesystem on dev %s.\n",
-			bdevname(sb->s_dev));
+			sb->s_id);
 	}
 out_fail:
 	if (opts.iocharset) {
@@ -973,7 +973,7 @@
 	}
 	lock_kernel();
 	if (!(bh = fat_bread(sb, i_pos >> MSDOS_SB(sb)->dir_per_block_bits))) {
-		printk("dev = %s, ino = %d\n", kdevname(inode->i_dev), i_pos);
+		printk("dev = %s, ino = %d\n", sb->s_id, i_pos);
 		fat_fs_panic(sb, "msdos_write_inode: unable to read i-node block");
 		unlock_kernel();
 		return;
diff -urN linux-2.5.2-pre7/fs/fat/misc.c linux/fs/fat/misc.c
--- linux-2.5.2-pre7/fs/fat/misc.c	Fri Oct 12 13:48:42 2001
+++ linux/fs/fat/misc.c	Fri Jan  4 19:01:22 2002
@@ -44,7 +44,7 @@
 
 	not_ro = !(s->s_flags & MS_RDONLY);
 	if (not_ro) s->s_flags |= MS_RDONLY;
-	printk("Filesystem panic (dev %s).\n  %s\n", kdevname(s->s_dev), msg);
+	printk("Filesystem panic (dev %s).\n  %s\n", s->s_id, msg);
 	if (not_ro)
 		printk("  File system has been set read-only\n");
 }
diff -urN linux-2.5.2-pre7/fs/freevxfs/vxfs.h linux/fs/freevxfs/vxfs.h
--- linux-2.5.2-pre7/fs/freevxfs/vxfs.h	Mon May 21 12:31:06 2001
+++ linux/fs/freevxfs/vxfs.h	Fri Jan  4 19:01:22 2002
@@ -30,7 +30,7 @@
 #ifndef _VXFS_SUPER_H_
 #define _VXFS_SUPER_H_
 
-#ident "$Id: vxfs.h 1.11 2001/05/21 15:40:28 hch Exp hch $"
+#ident "$Id: vxfs.h 1.12 2001/12/28 19:48:03 hch Exp $"
 
 /*
  * Veritas filesystem driver - superblock structure.
@@ -39,6 +39,7 @@
  * superblocks of the Veritas Filesystem.
  */
 #include <linux/types.h>
+#include "vxfs_kcompat.h"
 
 
 /*
diff -urN linux-2.5.2-pre7/fs/freevxfs/vxfs_bmap.c linux/fs/freevxfs/vxfs_bmap.c
--- linux-2.5.2-pre7/fs/freevxfs/vxfs_bmap.c	Sun Dec 16 12:23:00 2001
+++ linux/fs/freevxfs/vxfs_bmap.c	Fri Jan  4 19:01:22 2002
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  */
 
-#ident "$Id: vxfs_bmap.c,v 1.23 2001/07/05 19:48:03 hch Exp hch $"
+#ident "$Id: vxfs_bmap.c,v 1.25 2002/01/02 23:36:55 hch Exp hch $"
 
 /*
  * Veritas filesystem driver - filesystem to disk block mapping.
@@ -64,48 +64,46 @@
  *   The physical block number on success, else Zero.
  */
 static daddr_t
-vxfs_bmap_ext4(struct inode *ip, long iblock)
+vxfs_bmap_ext4(struct inode *ip, long bn)
 {
-	struct vxfs_inode_info		*vip = VXFS_INO(ip);
-	struct super_block		*sbp = ip->i_sb;
-	kdev_t				dev = ip->i_dev;
-	u_long				bsize = sbp->s_blocksize;
-	long				size = 0;
-	int				i;
+	struct super_block *sb = ip->i_sb;
+	struct vxfs_inode_info *vip = VXFS_INO(ip);
+	unsigned long bsize = sb->s_blocksize;
+	u32 indsize = vip->vii_ext4.ve4_indsize;
+	int i;
 
-	for (i = 0; i < VXFS_NDADDR; i++) {
-		struct direct		*dp = vip->vii_ext4.ve4_direct + i;
-
-#ifdef DIAGNOSTIC
-		printk(KERN_DEBUG "iblock: %ld, %d (size: %lu)\n", iblock, i, size);
-		printk(KERN_DEBUG "dp->extent: %d, dp->size: %d\n", dp->extent, dp->size);
-#endif
+	if (indsize > sb->s_blocksize)
+		goto fail_size;
 
-		if (iblock >= size && iblock < (size + dp->size))
-			return ((iblock - size) + dp->extent);
-		size += dp->size;
+	for (i = 0; i < VXFS_NDADDR; i++) {
+		struct direct *d = vip->vii_ext4.ve4_direct + i;
+		if (bn >= 0 && bn < d->size)
+			return (bn + d->extent);
+		bn -= d->size;
 	}
 
-	iblock -= size;
+	if ((bn / (indsize * indsize * bsize / 4)) == 0) {
+		struct buffer_head *buf;
+		daddr_t	bno;
+		u32 *indir;
+
+		buf = sb_bread(sb, vip->vii_ext4.ve4_indir[0]);
+		if (!buf || !buffer_mapped(buf))
+			goto fail_buf;
 
-	if (!(iblock / (vip->vii_ext4.ve4_indsize * vip->vii_ext4.ve4_indsize * bsize >> 2))) {
-		struct buffer_head	*bp;
-		daddr_t			pblock;
-
-		/*
-		 * XXX: is the second indir only used for
-		 *	double indirect extents?
-		 */
-		bp = bread(dev, vip->vii_ext4.ve4_indir[0],
-				bsize * ((vip->vii_ext4.ve4_indsize) / bsize) + 1);
-		pblock = *(bp->b_data + ((iblock / vip->vii_ext4.ve4_indsize) %
-					(vip->vii_ext4.ve4_indsize * bsize)));
+		indir = (u32 *)buf->b_data;
+		bno = indir[(bn/indsize) % (indsize*bn)] + (bn%indsize);
 
-		brelse(bp);
-		return (pblock + (iblock % vip->vii_ext4.ve4_indsize));
+		brelse(buf);
+		return bno;
 	} else
 		printk(KERN_WARNING "no matching indir?");
 
+	return 0;
+
+fail_size:
+	printk("vxfs: indirect extent to big!\n");
+fail_buf:
 	return 0;
 }
 
diff -urN linux-2.5.2-pre7/fs/freevxfs/vxfs_extern.h linux/fs/freevxfs/vxfs_extern.h
--- linux-2.5.2-pre7/fs/freevxfs/vxfs_extern.h	Sun Sep  2 10:34:36 2001
+++ linux/fs/freevxfs/vxfs_extern.h	Fri Jan  4 19:01:22 2002
@@ -30,7 +30,7 @@
 #ifndef _VXFS_EXTERN_H_
 #define _VXFS_EXTERN_H_
 
-#ident "$Id: vxfs_extern.h,v 1.21 2001/08/07 16:13:30 hch Exp hch $"
+#ident "$Id: vxfs_extern.h,v 1.22 2001/12/28 20:50:47 hch Exp hch $"
 
 /*
  * Veritas filesystem driver - external prototypes.
@@ -71,7 +71,7 @@
 extern int			vxfs_read_olt(struct super_block *, u_long);
 
 /* vxfs_subr.c */
-extern struct page *		vxfs_get_page(struct inode *, u_long);
+extern struct page *		vxfs_get_page(struct address_space *, u_long);
 extern __inline__ void		vxfs_put_page(struct page *);
 extern struct buffer_head *	vxfs_bread(struct inode *, int);
 
diff -urN linux-2.5.2-pre7/fs/freevxfs/vxfs_fshead.c linux/fs/freevxfs/vxfs_fshead.c
--- linux-2.5.2-pre7/fs/freevxfs/vxfs_fshead.c	Sun Sep  2 10:34:36 2001
+++ linux/fs/freevxfs/vxfs_fshead.c	Fri Jan  4 19:01:22 2002
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  */
 
-#ident "$Id: vxfs_fshead.c,v 1.19 2001/08/07 16:14:10 hch Exp hch $"
+#ident "$Id: vxfs_fshead.c,v 1.20 2002/01/02 22:02:12 hch Exp hch $"
 
 /*
  * Veritas filesystem driver - fileset header routines.
@@ -81,9 +81,9 @@
 	if (buffer_mapped(bp)) {
 		struct vxfs_fsh		*fhp;
 
-		if (!(fhp = kmalloc(sizeof(struct vxfs_fsh), SLAB_KERNEL)))
+		if (!(fhp = kmalloc(sizeof(*fhp), SLAB_KERNEL)))
 			return NULL;
-		memcpy(fhp, bp->b_data, sizeof(struct vxfs_fsh));
+		memcpy(fhp, bp->b_data, sizeof(*fhp));
 
 		brelse(bp);
 		return (fhp);
diff -urN linux-2.5.2-pre7/fs/freevxfs/vxfs_inode.c linux/fs/freevxfs/vxfs_inode.c
--- linux-2.5.2-pre7/fs/freevxfs/vxfs_inode.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/freevxfs/vxfs_inode.c	Fri Jan  4 19:01:22 2002
@@ -27,15 +27,15 @@
  * SUCH DAMAGE.
  */
 
-#ident "$Id: vxfs_inode.c,v 1.37 2001/08/07 16:13:30 hch Exp hch $"
+#ident "$Id: vxfs_inode.c,v 1.42 2002/01/02 23:51:36 hch Exp hch $"
 
 /*
  * Veritas filesystem driver - inode routines.
  */
 #include <linux/fs.h>
+#include <linux/pagemap.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
-#include <linux/pagemap.h>
 
 #include "vxfs.h"
 #include "vxfs_inode.h"
@@ -48,6 +48,7 @@
 extern struct inode_operations vxfs_immed_symlink_iops;
 
 static struct file_operations vxfs_file_operations = {
+	.open =			generic_file_open,
 	.llseek =		generic_file_llseek,
 	.read =			generic_file_read,
 	.mmap =			generic_file_mmap,
@@ -114,7 +115,7 @@
 		if (!(vip = kmem_cache_alloc(vxfs_inode_cachep, SLAB_KERNEL)))
 			goto fail;
 		dip = (struct vxfs_dinode *)(bp->b_data + offset);
-		memcpy(vip, dip, sizeof(struct vxfs_inode_info));
+		memcpy(vip, dip, sizeof(*vip));
 #ifdef DIAGNOSTIC
 		vxfs_dumpi(vip, ino);
 #endif
@@ -146,7 +147,7 @@
 	u_long				offset;
 
 	offset = (ino % (PAGE_SIZE / VXFS_ISIZE)) * VXFS_ISIZE;
-	pp = vxfs_get_page(ilistp, ino * VXFS_ISIZE / PAGE_SIZE);
+	pp = vxfs_get_page(ilistp->i_mapping, ino * VXFS_ISIZE / PAGE_SIZE);
 
 	if (!IS_ERR(pp)) {
 		struct vxfs_inode_info	*vip;
@@ -156,7 +157,7 @@
 		if (!(vip = kmem_cache_alloc(vxfs_inode_cachep, SLAB_KERNEL)))
 			goto fail;
 		dip = (struct vxfs_dinode *)(kaddr + offset);
-		memcpy(vip, dip, sizeof(struct vxfs_inode_info));
+		memcpy(vip, dip, sizeof(*vip));
 #ifdef DIAGNOSTIC
 		vxfs_dumpi(vip, ino);
 #endif
diff -urN linux-2.5.2-pre7/fs/freevxfs/vxfs_kcompat.h linux/fs/freevxfs/vxfs_kcompat.h
--- linux-2.5.2-pre7/fs/freevxfs/vxfs_kcompat.h	Wed Dec 31 16:00:00 1969
+++ linux/fs/freevxfs/vxfs_kcompat.h	Fri Jan  4 19:01:22 2002
@@ -0,0 +1,49 @@
+#ifndef _VXFS_KCOMPAT_H
+#define _VXFS_KCOMPAT_H
+
+#include <linux/version.h>
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
+
+#include <linux/blkdev.h>
+
+typedef long sector_t;
+
+/* From include/linux/fs.h (Linux 2.5.2-pre3)  */
+static inline struct buffer_head * sb_bread(struct super_block *sb, int block)
+{
+	return bread(sb->s_dev, block, sb->s_blocksize);
+}
+
+/* Dito.  */
+static inline void map_bh(struct buffer_head *bh, struct super_block *sb, int block)
+{
+	bh->b_state |= 1 << BH_Mapped;
+	bh->b_dev = sb->s_dev;
+	bh->b_blocknr = block;
+}
+
+/* From fs/block_dev.c (Linux 2.5.2-pre2)  */
+static inline int sb_set_blocksize(struct super_block *sb, int size)
+{
+	int bits;
+	if (set_blocksize(sb->s_dev, size) < 0)
+		return 0;
+	sb->s_blocksize = size;
+	for (bits = 9, size >>= 9; size >>= 1; bits++)
+		;
+	sb->s_blocksize_bits = bits;
+	return sb->s_blocksize;
+}
+
+/* Dito.  */
+static inline int sb_min_blocksize(struct super_block *sb, int size)
+{
+	int minsize = get_hardsect_size(sb->s_dev);
+	if (size < minsize)
+		size = minsize;
+	return sb_set_blocksize(sb, size);
+}
+
+#endif /* Kernel 2.4 */
+#endif /* _VXFS_KCOMPAT_H */
diff -urN linux-2.5.2-pre7/fs/freevxfs/vxfs_lookup.c linux/fs/freevxfs/vxfs_lookup.c
--- linux-2.5.2-pre7/fs/freevxfs/vxfs_lookup.c	Wed Jun 27 17:10:55 2001
+++ linux/fs/freevxfs/vxfs_lookup.c	Fri Jan  4 19:01:22 2002
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  */
 
-#ident "$Id: vxfs_lookup.c,v 1.19 2001/05/30 19:50:20 hch Exp hch $"
+#ident "$Id: vxfs_lookup.c,v 1.21 2002/01/02 22:00:13 hch Exp hch $"
 
 /*
  * Veritas filesystem driver - lookup and other directory related code.
@@ -126,7 +126,7 @@
 		caddr_t			kaddr;
 		struct page		*pp;
 
-		pp = vxfs_get_page(ip, page);
+		pp = vxfs_get_page(ip->i_mapping, page);
 		if (IS_ERR(pp))
 			continue;
 		kaddr = (caddr_t)page_address(pp);
@@ -273,7 +273,7 @@
 		caddr_t			kaddr;
 		struct page		*pp;
 
-		pp = vxfs_get_page(ip, page);
+		pp = vxfs_get_page(ip->i_mapping, page);
 		if (IS_ERR(pp))
 			continue;
 		kaddr = (caddr_t)page_address(pp);
@@ -318,6 +318,5 @@
 done:
 	fp->f_pos = ((page << PAGE_CACHE_SHIFT) | offset) + 2;
 out:
-	fp->f_version = ip->i_version;
 	return 0;
 }
diff -urN linux-2.5.2-pre7/fs/freevxfs/vxfs_olt.c linux/fs/freevxfs/vxfs_olt.c
--- linux-2.5.2-pre7/fs/freevxfs/vxfs_olt.c	Sun Sep  2 10:34:36 2001
+++ linux/fs/freevxfs/vxfs_olt.c	Fri Jan  4 19:01:22 2002
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  */
 
-#ident "$Id: vxfs_olt.c,v 1.9 2001/08/07 16:14:45 hch Exp hch $"
+#ident "$Id: vxfs_olt.c,v 1.10 2002/01/02 23:03:58 hch Exp hch $"
 
 /* 
  * Veritas filesystem driver - object location table support.
@@ -85,14 +85,23 @@
 	char			*oaddr, *eaddr;
 
 
-	bp = bread(sbp->s_dev,
-			vxfs_oblock(sbp, infp->vsi_oltext, bsize), bsize);
+	bp = sb_bread(sbp, vxfs_oblock(sbp, infp->vsi_oltext, bsize));
 	if (!bp || !bp->b_data)
 		goto fail;
 
 	op = (struct vxfs_olt *)bp->b_data;
 	if (op->olt_magic != VXFS_OLT_MAGIC) {
 		printk(KERN_NOTICE "vxfs: ivalid olt magic number\n");
+		goto fail;
+	}
+
+	/*
+	 * It is in theory possible that vsi_oltsize is > 1.
+	 * I've not seen any such filesystem yet and I'm lazy..  --hch
+	 */
+	if (infp->vsi_oltsize > 1) {
+		printk(KERN_NOTICE "vxfs: oltsize > 1 detected.\n");
+		printk(KERN_NOTICE "vxfs: please notify hch@caldera.de\n");
 		goto fail;
 	}
 
diff -urN linux-2.5.2-pre7/fs/freevxfs/vxfs_subr.c linux/fs/freevxfs/vxfs_subr.c
--- linux-2.5.2-pre7/fs/freevxfs/vxfs_subr.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/freevxfs/vxfs_subr.c	Fri Jan  4 19:01:22 2002
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  */
 
-#ident "$Id: vxfs_subr.c,v 1.5 2001/04/26 22:49:51 hch Exp hch $"
+#ident "$Id: vxfs_subr.c,v 1.8 2001/12/28 20:50:47 hch Exp hch $"
 
 /*
  * Veritas filesystem driver - shared subroutines.
@@ -37,6 +37,7 @@
 #include <linux/slab.h>
 #include <linux/pagemap.h>
 
+#include "vxfs_kcompat.h"
 #include "vxfs_extern.h"
 
 
@@ -62,9 +63,8 @@
  *   The wanted page on success, else a NULL pointer.
  */
 struct page *
-vxfs_get_page(struct inode *ip, u_long n)
+vxfs_get_page(struct address_space *mapping, u_long n)
 {
-	struct address_space *		mapping = ip->i_mapping;
 	struct page *			pp;
 
 	pp = read_cache_page(mapping, n,
diff -urN linux-2.5.2-pre7/fs/freevxfs/vxfs_super.c linux/fs/freevxfs/vxfs_super.c
--- linux-2.5.2-pre7/fs/freevxfs/vxfs_super.c	Thu Oct 11 09:43:38 2001
+++ linux/fs/freevxfs/vxfs_super.c	Fri Jan  4 19:01:22 2002
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  */
 
-#ident "$Id: vxfs_super.c,v 1.26 2001/08/07 16:13:30 hch Exp hch $"
+#ident "$Id: vxfs_super.c,v 1.29 2002/01/02 22:02:12 hch Exp hch $"
 
 /*
  * Veritas filesystem driver - superblock related routines.
@@ -62,19 +62,6 @@
 	.statfs =		vxfs_statfs,
 };
 
-static __inline__ u_long
-vxfs_validate_bsize(kdev_t dev)
-{
-	u_long			bsize;
-	
-	bsize = get_hardsect_size(dev);
-	if (bsize < BLOCK_SIZE)
-		bsize = BLOCK_SIZE;
-
-	set_blocksize(dev, bsize);
-	return (bsize);
-}
-
 /**
  * vxfs_put_super - free superblock resources
  * @sbp:	VFS superblock.
@@ -153,21 +140,24 @@
 {
 	struct vxfs_sb_info	*infp;
 	struct vxfs_sb		*rsbp;
-	struct buffer_head	*bp;
+	struct buffer_head	*bp = NULL;
 	u_long			bsize;
-	kdev_t			dev = sbp->s_dev;
 
-	infp = kmalloc(sizeof(struct vxfs_sb_info), GFP_KERNEL);
+	infp = kmalloc(sizeof(*infp), GFP_KERNEL);
 	if (!infp) {
 		printk(KERN_WARNING "vxfs: unable to allocate incore superblock\n");
 		return NULL;
 	}
-	memset(infp, 0, sizeof(struct vxfs_sb_info));
+	memset(infp, 0, sizeof(*infp));
 
-	bsize = vxfs_validate_bsize(dev);
+	bsize = sb_min_blocksize(sbp, BLOCK_SIZE);
+	if (!bsize) {
+		printk(KERN_WARNING "vxfs: unable to set blocksize\n");
+		goto out;
+	}
 
-	bp = bread(dev, 1, bsize);
-	if (!bp) {
+	bp = sb_bread(sbp, 1);
+	if (!bp || !buffer_mapped(bp)) {
 		if (!silent) {
 			printk(KERN_WARNING
 				"vxfs: unable to read disk superblock\n");
@@ -194,31 +184,15 @@
 #endif
 
 	sbp->s_magic = rsbp->vs_magic;
-	sbp->s_blocksize = rsbp->vs_bsize;
 	sbp->u.generic_sbp = (void *)infp;
 
 	infp->vsi_raw = rsbp;
 	infp->vsi_bp = bp;
 	infp->vsi_oltext = rsbp->vs_oltext[0];
 	infp->vsi_oltsize = rsbp->vs_oltsize;
-	
 
-	switch (rsbp->vs_bsize) {
-	case 1024:
-		sbp->s_blocksize_bits = 10;
-		break;
-	case 2048:
-		sbp->s_blocksize_bits = 11;
-		break;
-	case 4096:
-		sbp->s_blocksize_bits = 12;
-		break;
-	default:
-		if (!silent) {
-			printk(KERN_WARNING
-				"vxfs: unsupported blocksise: %d\n",
-				rsbp->vs_bsize);
-		}
+	if (!sb_set_blocksize(sbp, rsbp->vs_bsize)) {
+		printk(KERN_WARNING "vxfs: unable to set final block size\n");
 		goto out;
 	}
 
diff -urN linux-2.5.2-pre7/fs/hfs/inode.c linux/fs/hfs/inode.c
--- linux-2.5.2-pre7/fs/hfs/inode.c	Wed Sep 12 15:34:06 2001
+++ linux/fs/hfs/inode.c	Fri Jan  4 19:01:22 2002
@@ -311,14 +311,11 @@
 	        return NULL;
 	}
 
-	if (inode->i_dev != sb->s_dev) {
-	        iput(inode); /* automatically does an hfs_cat_put */
-		inode = NULL;
-	} else if (!inode->i_mode || (*sys_entry == NULL)) {
+	if (!inode->i_mode || (*sys_entry == NULL)) {
 		/* Initialize the inode */
 		struct hfs_sb_info *hsb = HFS_SB(sb);
 
-		inode->i_rdev = 0;
+		inode->i_rdev = NODEV;
 		inode->i_ctime = inode->i_atime = inode->i_mtime =
 					hfs_m_to_utime(entry->modify_date);
 		inode->i_blksize = HFS_SECTOR_SIZE;
diff -urN linux-2.5.2-pre7/fs/hfs/super.c linux/fs/hfs/super.c
--- linux-2.5.2-pre7/fs/hfs/super.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/hfs/super.c	Fri Jan  4 19:01:22 2002
@@ -425,7 +425,7 @@
 	if (!mdb) {
 		if (!silent) {
 			hfs_warn("VFS: Can't find a HFS filesystem on dev %s.\n",
-			       kdevname(dev));
+			       s->s_id);
 		}
 		goto bail2;
 	}
diff -urN linux-2.5.2-pre7/fs/hpfs/super.c linux/fs/hpfs/super.c
--- linux-2.5.2-pre7/fs/hpfs/super.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/hpfs/super.c	Fri Jan  4 19:01:22 2002
@@ -362,7 +362,6 @@
 struct super_block *hpfs_read_super(struct super_block *s, void *options,
 				    int silent)
 {
-	kdev_t dev;
 	struct buffer_head *bh0, *bh1, *bh2;
 	struct hpfs_boot_block *bootblock;
 	struct hpfs_super_block *superblock;
@@ -408,7 +407,6 @@
 	}
 
 	/*s->s_hpfs_mounting = 1;*/
-	dev = s->s_dev;
 	sb_set_blocksize(s, 512);
 	s->s_hpfs_fs_size = -1;
 	if (!(bootblock = hpfs_map_sector(s, 0, &bh0, 0))) goto bail1;
diff -urN linux-2.5.2-pre7/fs/isofs/inode.c linux/fs/isofs/inode.c
--- linux-2.5.2-pre7/fs/isofs/inode.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/isofs/inode.c	Fri Jan  4 19:01:22 2002
@@ -469,7 +469,6 @@
 static struct super_block *isofs_read_super(struct super_block *s, void *data,
 					    int silent)
 {
-	kdev_t				dev = s->s_dev;
 	struct buffer_head	      * bh = NULL, *pri_bh = NULL;
 	struct hs_primary_descriptor  * h_pri = NULL;
 	struct iso_primary_descriptor * pri = NULL;
@@ -820,7 +819,7 @@
 out_no_read:
 	printk(KERN_WARNING "isofs_read_super: "
 		"bread failed, dev=%s, iso_blknum=%d, block=%d\n",
-		kdevname(dev), iso_blknum, block);
+		s->s_id, iso_blknum, block);
 	goto out_unlock;
 out_bad_zone_size:
 	printk(KERN_WARNING "Bad logical zone size %ld\n",
diff -urN linux-2.5.2-pre7/fs/jbd/journal.c linux/fs/jbd/journal.c
--- linux-2.5.2-pre7/fs/jbd/journal.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/jbd/journal.c	Fri Jan  4 19:01:22 2002
@@ -774,7 +774,7 @@
 	journal->j_inode = inode;
 	jbd_debug(1,
 		  "journal %p: inode %s/%ld, size %Ld, bits %d, blksize %ld\n",
-		  journal, bdevname(inode->i_dev), inode->i_ino, 
+		  journal, inode->i_sb->s_id, inode->i_ino, 
 		  (long long) inode->i_size,
 		  inode->i_sb->s_blocksize_bits, inode->i_sb->s_blocksize);
 
diff -urN linux-2.5.2-pre7/fs/jffs/inode-v23.c linux/fs/jffs/inode-v23.c
--- linux-2.5.2-pre7/fs/jffs/inode-v23.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/jffs/inode-v23.c	Fri Jan  4 19:01:22 2002
@@ -79,7 +79,7 @@
 	struct jffs_control *c;
 
 	D1(printk(KERN_NOTICE "JFFS: Trying to mount device %s.\n",
-		  kdevname(dev)));
+		  sb->s_id));
 
 	if (major(dev) != MTD_BLOCK_MAJOR) {
 		printk(KERN_WARNING "JFFS: Trying to mount a "
@@ -119,7 +119,7 @@
 	if (jffs_register_jffs_proc_dir(dev, c) < 0) {
 		printk(KERN_WARNING "JFFS: Failed to initialize the JFFS "
 			"proc file system for device %s.\n",
-			kdevname(dev));
+			sb->s_id);
 	}
 #endif
 
@@ -144,7 +144,7 @@
 	D1(printk(KERN_NOTICE "JFFS: GC thread pid=%d.\n", (int) c->thread_pid));
 
 	D1(printk(KERN_NOTICE "JFFS: Successfully mounted device %s.\n",
-	       kdevname(dev)));
+	       sb->s_id));
 	return sb;
 
 jffs_sb_err3:
@@ -153,7 +153,7 @@
 	jffs_cleanup_control((struct jffs_control *)sb->u.generic_sbp);
 jffs_sb_err1:
 	printk(KERN_WARNING "JFFS: Failed to mount device %s.\n",
-	       kdevname(dev));
+	       sb->s_id);
 	return 0;
 }
 
@@ -163,7 +163,6 @@
 jffs_put_super(struct super_block *sb)
 {
 	struct jffs_control *c = (struct jffs_control *) sb->u.generic_sbp;
-	D1(kdev_t dev = sb->s_dev);
 
 	D2(printk("jffs_put_super()\n"));
 
@@ -181,7 +180,7 @@
 
 	jffs_cleanup_control((struct jffs_control *)sb->u.generic_sbp);
 	D1(printk(KERN_NOTICE "JFFS: Successfully unmounted device %s.\n",
-	       kdevname(dev)));
+	       sb->s_id));
 }
 
 
diff -urN linux-2.5.2-pre7/fs/jffs2/super.c linux/fs/jffs2/super.c
--- linux-2.5.2-pre7/fs/jffs2/super.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/jffs2/super.c	Fri Jan  4 19:01:22 2002
@@ -197,7 +197,7 @@
 	struct inode *root_i;
 	int i;
 
-	D1(printk(KERN_DEBUG "jffs2: read_super for device %s\n", kdevname(sb->s_dev)));
+	D1(printk(KERN_DEBUG "jffs2: read_super for device %s\n", sb->s_id));
 
 	if (major(sb->s_dev) != MTD_BLOCK_MAJOR) {
 		if (!silent)
diff -urN linux-2.5.2-pre7/fs/minix/bitmap.c linux/fs/minix/bitmap.c
--- linux-2.5.2-pre7/fs/minix/bitmap.c	Sun Dec 16 12:23:05 2001
+++ linux/fs/minix/bitmap.c	Fri Jan  4 19:01:22 2002
@@ -74,7 +74,7 @@
 	bh = sb->u.minix_sb.s_zmap[zone];
 	if (!minix_test_and_clear_bit(bit,bh->b_data))
 		printk("free_block (%s:%d): bit already cleared\n",
-		       kdevname(sb->s_dev), block);
+		       sb->s_id, block);
 	mark_buffer_dirty(bh);
 	return;
 }
@@ -127,7 +127,7 @@
 
 	if (!ino || ino > sbi->s_ninodes) {
 		printk("Bad inode number on dev %s: %ld is out of range\n",
-		       bdevname(sb->s_dev), ino);
+		       sb->s_id, ino);
 		return NULL;
 	}
 	ino--;
@@ -152,7 +152,7 @@
 	*bh = NULL;
 	if (!ino || ino > sbi->s_ninodes) {
 		printk("Bad inode number on dev %s: %ld is out of range\n",
-		       bdevname(sb->s_dev), ino);
+		       sb->s_id, ino);
 		return NULL;
 	}
 	ino--;
diff -urN linux-2.5.2-pre7/fs/minix/inode.c linux/fs/minix/inode.c
--- linux-2.5.2-pre7/fs/minix/inode.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/minix/inode.c	Fri Jan  4 19:01:22 2002
@@ -258,7 +258,7 @@
 out_no_fs:
 	if (!silent)
 		printk("VFS: Can't find a Minix or Minix V2 filesystem on device "
-		       "%s.\n", bdevname(s->s_dev));
+		       "%s.\n", s->s_id);
     out_release:
 	brelse(bh);
 	goto out;
@@ -488,9 +488,8 @@
 		wait_on_buffer(bh);
 		if (buffer_req(bh) && !buffer_uptodate(bh))
 		{
-			printk ("IO error syncing minix inode ["
-				"%s:%08lx]\n",
-				kdevname(inode->i_dev), inode->i_ino);
+			printk ("IO error syncing minix inode [%s:%08lx]\n",
+				inode->i_sb->s_id, inode->i_ino);
 			err = -1;
 		}
 	}
diff -urN linux-2.5.2-pre7/fs/nfs/dir.c linux/fs/nfs/dir.c
--- linux-2.5.2-pre7/fs/nfs/dir.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/nfs/dir.c	Fri Jan  4 19:01:22 2002
@@ -643,8 +643,8 @@
 	struct nfs_fh fhandle;
 	int error;
 
-	dfprintk(VFS, "NFS: create(%x:%x/%ld, %s\n",
-		major(dir->i_dev), minor(dir->i_dev), dir->i_ino, dentry->d_name.name);
+	dfprintk(VFS, "NFS: create(%s/%ld, %s\n", dir->i_sb->s_id, 
+		dir->i_ino, dentry->d_name.name);
 
 	attr.ia_mode = mode;
 	attr.ia_valid = ATTR_MODE;
@@ -675,8 +675,8 @@
 	struct nfs_fh fhandle;
 	int error;
 
-	dfprintk(VFS, "NFS: mknod(%x:%x/%ld, %s\n",
-		major(dir->i_dev), minor(dir->i_dev), dir->i_ino, dentry->d_name.name);
+	dfprintk(VFS, "NFS: mknod(%s/%ld, %s\n", dir->i_sb->s_id,
+		dir->i_ino, dentry->d_name.name);
 
 	attr.ia_mode = mode;
 	attr.ia_valid = ATTR_MODE;
@@ -701,8 +701,8 @@
 	struct nfs_fh fhandle;
 	int error;
 
-	dfprintk(VFS, "NFS: mkdir(%x:%x/%ld, %s\n",
-		major(dir->i_dev), minor(dir->i_dev), dir->i_ino, dentry->d_name.name);
+	dfprintk(VFS, "NFS: mkdir(%s/%ld, %s\n", dir->i_sb->s_id,
+		dir->i_ino, dentry->d_name.name);
 
 	attr.ia_valid = ATTR_MODE;
 	attr.ia_mode = mode | S_IFDIR;
@@ -730,8 +730,8 @@
 {
 	int error;
 
-	dfprintk(VFS, "NFS: rmdir(%x:%x/%ld, %s\n",
-		major(dir->i_dev), minor(dir->i_dev), dir->i_ino, dentry->d_name.name);
+	dfprintk(VFS, "NFS: rmdir(%s/%ld, %s\n", dir->i_sb->s_id,
+		dir->i_ino, dentry->d_name.name);
 
 	nfs_zap_caches(dir);
 	error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name);
@@ -877,8 +877,8 @@
 {
 	int error;
 
-	dfprintk(VFS, "NFS: unlink(%x:%x/%ld, %s)\n",
-		major(dir->i_dev), minor(dir->i_dev), dir->i_ino, dentry->d_name.name);
+	dfprintk(VFS, "NFS: unlink(%s/%ld, %s)\n", dir->i_sb->s_id,
+		dir->i_ino, dentry->d_name.name);
 
 	error = nfs_sillyrename(dir, dentry);
 	if (error && error != -EBUSY) {
@@ -900,8 +900,8 @@
 	unsigned int maxlen;
 	int error;
 
-	dfprintk(VFS, "NFS: symlink(%x:%x/%ld, %s, %s)\n",
-		major(dir->i_dev), minor(dir->i_dev), dir->i_ino, dentry->d_name.name, symname);
+	dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s)\n", dir->i_sb->s_id,
+		dir->i_ino, dentry->d_name.name, symname);
 
 	error = -ENAMETOOLONG;
 	maxlen = (NFS_PROTO(dir)->version==2) ? NFS2_MAXPATHLEN : NFS3_MAXPATHLEN;
diff -urN linux-2.5.2-pre7/fs/nfs/file.c linux/fs/nfs/file.c
--- linux-2.5.2-pre7/fs/nfs/file.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/nfs/file.c	Fri Jan  4 19:01:22 2002
@@ -73,8 +73,7 @@
 	struct inode	*inode = file->f_dentry->d_inode;
 	int		status;
 
-	dfprintk(VFS, "nfs: flush(%02x:%02x/%ld)\n",
-		major(inode->i_dev), minor(inode->i_dev), inode->i_ino);
+	dfprintk(VFS, "nfs: flush(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino);
 
 	/* Make sure all async reads have been sent off. We don't bother
 	 * waiting on them though... */
@@ -133,8 +132,7 @@
 	struct inode *inode = dentry->d_inode;
 	int status;
 
-	dfprintk(VFS, "nfs: fsync(%02x:%02x/%ld)\n",
-		major(inode->i_dev), minor(inode->i_dev), inode->i_ino);
+	dfprintk(VFS, "nfs: fsync(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino);
 
 	lock_kernel();
 	status = nfs_wb_file(inode, file);
@@ -247,8 +245,8 @@
 	struct inode * inode = filp->f_dentry->d_inode;
 	int	status = 0;
 
-	dprintk("NFS: nfs_lock(f=%02x:%02x/%ld, t=%x, fl=%x, r=%Ld:%Ld)\n",
-			major(inode->i_dev), minor(inode->i_dev), inode->i_ino,
+	dprintk("NFS: nfs_lock(f=%s/%ld, t=%x, fl=%x, r=%Ld:%Ld)\n",
+			inode->i_sb->s_id, inode->i_ino,
 			fl->fl_type, fl->fl_flags,
 			(long long)fl->fl_start, (long long)fl->fl_end);
 
diff -urN linux-2.5.2-pre7/fs/nfs/inode.c linux/fs/nfs/inode.c
--- linux-2.5.2-pre7/fs/nfs/inode.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/nfs/inode.c	Fri Jan  4 19:01:22 2002
@@ -127,7 +127,7 @@
 static void
 nfs_delete_inode(struct inode * inode)
 {
-	dprintk("NFS: delete_inode(%x:%x/%ld)\n", major(inode->i_dev), minor(inode->i_dev), inode->i_ino);
+	dprintk("NFS: delete_inode(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino);
 
 	/*
 	 * The following can never actually happen...
@@ -271,6 +271,9 @@
 	struct nfs_fsinfo	fsinfo;
 	int			tcp, version, maxlen;
 
+	/* We probably want something more informative here */
+	snprintf(sb->s_id, sizeof(sb->s_id), "%x:%x", major(sb->s_dev), minor(sb->s_dev));
+
 	memset(&sb->u.nfs_sb, 0, sizeof(sb->u.nfs_sb));
 	if (!data)
 		goto out_miss_args;
@@ -747,8 +750,8 @@
 		goto out_no_inode;
 
 	nfs_fill_inode(inode, fh, fattr);
-	dprintk("NFS: __nfs_fhget(%x:%x/%Ld ct=%d)\n",
-		major(inode->i_dev), minor(inode->i_dev),
+	dprintk("NFS: __nfs_fhget(%s/%Ld ct=%d)\n",
+		inode->i_sb->s_id,
 		(long long)NFS_FILEID(inode),
 		atomic_read(&inode->i_count));
 
@@ -903,9 +906,8 @@
 	int		 status = -ESTALE;
 	struct nfs_fattr fattr;
 
-	dfprintk(PAGECACHE, "NFS: revalidating (%x:%x/%Ld)\n",
-		major(inode->i_dev), minor(inode->i_dev),
-		(long long)NFS_FILEID(inode));
+	dfprintk(PAGECACHE, "NFS: revalidating (%s/%Ld)\n",
+		inode->i_sb->s_id, (long long)NFS_FILEID(inode));
 
 	lock_kernel();
 	if (!inode || is_bad_inode(inode))
@@ -926,8 +928,8 @@
 
 	status = NFS_PROTO(inode)->getattr(inode, &fattr);
 	if (status) {
-		dfprintk(PAGECACHE, "nfs_revalidate_inode: (%x:%x/%Ld) getattr failed, error=%d\n",
-			 major(inode->i_dev), minor(inode->i_dev),
+		dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) getattr failed, error=%d\n",
+			 inode->i_sb->s_id,
 			 (long long)NFS_FILEID(inode), status);
 		if (status == -ESTALE) {
 			NFS_FLAGS(inode) |= NFS_INO_STALE;
@@ -939,13 +941,13 @@
 
 	status = nfs_refresh_inode(inode, &fattr);
 	if (status) {
-		dfprintk(PAGECACHE, "nfs_revalidate_inode: (%x:%x/%Ld) refresh failed, error=%d\n",
-			 major(inode->i_dev), minor(inode->i_dev),
+		dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) refresh failed, error=%d\n",
+			 inode->i_sb->s_id,
 			 (long long)NFS_FILEID(inode), status);
 		goto out;
 	}
-	dfprintk(PAGECACHE, "NFS: (%x:%x/%Ld) revalidation complete\n",
-		major(inode->i_dev), minor(inode->i_dev),
+	dfprintk(PAGECACHE, "NFS: (%s/%Ld) revalidation complete\n",
+		inode->i_sb->s_id,
 		(long long)NFS_FILEID(inode));
 
 	NFS_FLAGS(inode) &= ~NFS_INO_STALE;
@@ -1005,8 +1007,8 @@
 	time_t		new_atime;
 	int		invalid = 0;
 
-	dfprintk(VFS, "NFS: refresh_inode(%x:%x/%ld ct=%d info=0x%x)\n",
-			major(inode->i_dev), minor(inode->i_dev), inode->i_ino,
+	dfprintk(VFS, "NFS: refresh_inode(%s/%ld ct=%d info=0x%x)\n",
+			inode->i_sb->s_id, inode->i_ino,
 			atomic_read(&inode->i_count), fattr->valid);
 
 	if (NFS_FSID(inode) != fattr->fsid ||
@@ -1044,7 +1046,7 @@
 	 */
 	if (NFS_CACHE_ISIZE(inode) != new_size) {
 #ifdef NFS_DEBUG_VERBOSE
-		printk(KERN_DEBUG "NFS: isize change on %x/%ld\n", inode->i_dev, inode->i_ino);
+		printk(KERN_DEBUG "NFS: isize change on %s/%ld\n", inode->i_sb->s_id, inode->i_ino);
 #endif
 		invalid = 1;
 	}
@@ -1056,7 +1058,7 @@
 	 */
 	if (NFS_CACHE_MTIME(inode) != new_mtime) {
 #ifdef NFS_DEBUG_VERBOSE
-		printk(KERN_DEBUG "NFS: mtime change on %x/%ld\n", inode->i_dev, inode->i_ino);
+		printk(KERN_DEBUG "NFS: mtime change on %s/%ld\n", inode->i_sb->s_id, inode->i_ino);
 #endif
 		invalid = 1;
 	}
diff -urN linux-2.5.2-pre7/fs/nfs/nfsroot.c linux/fs/nfs/nfsroot.c
--- linux-2.5.2-pre7/fs/nfs/nfsroot.c	Thu Oct 25 00:02:26 2001
+++ linux/fs/nfs/nfsroot.c	Fri Jan  4 19:01:22 2002
@@ -334,7 +334,7 @@
  */
 int __init nfs_root_setup(char *line)
 {
-	ROOT_DEV = MKDEV(UNNAMED_MAJOR, 255);
+	ROOT_DEV = mk_kdev(UNNAMED_MAJOR, 255);
 	if (line[0] == '/' || line[0] == ',' || (line[0] >= '0' && line[0] <= '9')) {
 		strncpy(nfs_root_name, line, sizeof(nfs_root_name));
 		nfs_root_name[sizeof(nfs_root_name)-1] = '\0';
diff -urN linux-2.5.2-pre7/fs/nfs/read.c linux/fs/nfs/read.c
--- linux-2.5.2-pre7/fs/nfs/read.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/nfs/read.c	Fri Jan  4 19:01:22 2002
@@ -108,9 +108,9 @@
 		if (count < rsize)
 			rsize = count;
 
-		dprintk("NFS: nfs_proc_read(%s, (%x:%x/%Ld), %Ld, %d, %p)\n",
+		dprintk("NFS: nfs_proc_read(%s, (%s/%Ld), %Ld, %d, %p)\n",
 			NFS_SERVER(inode)->hostname,
-			major(inode->i_dev), minor(inode->i_dev),
+			inode->i_sb->s_id,
 			(long long)NFS_FILEID(inode),
 			(long long)offset, rsize, buffer);
 
@@ -266,9 +266,9 @@
 	msg.rpc_cred = data->cred;
 
 	/* Start the async call */
-	dprintk("NFS: %4d initiated read call (req %x:%x/%Ld count %d nriov %d.\n",
+	dprintk("NFS: %4d initiated read call (req %s/%Ld count %d nriov %d.\n",
 		task->tk_pid,
-		major(inode->i_dev), minor(inode->i_dev),
+		inode->i_sb->s_id,
 		(long long)NFS_FILEID(inode),
 		data->args.count, data->args.nriov);
 
@@ -425,9 +425,8 @@
 		kunmap(page);
 		UnlockPage(page);
 
-		dprintk("NFS: read (%x:%x/%Ld %d@%Ld)\n",
-                        major(req->wb_inode->i_dev),
-                        minor(req->wb_inode->i_dev),
+		dprintk("NFS: read (%s/%Ld %d@%Ld)\n",
+                        req->wb_inode->i_sb->s_id,
                         (long long)NFS_FILEID(req->wb_inode),
                         req->wb_bytes,
                         (long long)(page_offset(page) + req->wb_offset));
diff -urN linux-2.5.2-pre7/fs/nfs/write.c linux/fs/nfs/write.c
--- linux-2.5.2-pre7/fs/nfs/write.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/nfs/write.c	Fri Jan  4 19:01:22 2002
@@ -159,8 +159,8 @@
 	if (!cred)
 		cred = get_rpccred(NFS_I(inode)->mm_cred);
 
-	dprintk("NFS:      nfs_writepage_sync(%x:%x/%Ld %d@%Ld)\n",
-		major(inode->i_dev), minor(inode->i_dev),
+	dprintk("NFS:      nfs_writepage_sync(%s/%Ld %d@%Ld)\n",
+		inode->i_sb->s_id,
 		(long long)NFS_FILEID(inode),
 		count, (long long)(page_offset(page) + offset));
 
@@ -935,9 +935,9 @@
 	msg.rpc_resp = &data->res;
 	msg.rpc_cred = data->cred;
 
-	dprintk("NFS: %4d initiated write call (req %x:%x/%Ld count %d nriov %d)\n",
+	dprintk("NFS: %4d initiated write call (req %s/%Ld count %d nriov %d)\n",
 		task->tk_pid, 
-		major(inode->i_dev), minor(inode->i_dev),
+		inode->i_sb->s_id,
 		(long long)NFS_FILEID(inode),
 		data->args.count, data->args.nriov);
 
@@ -1050,9 +1050,8 @@
 
 		kunmap(page);
 
-		dprintk("NFS: write (%x:%x/%Ld %d@%Ld)",
-			major(req->wb_inode->i_dev),
-			minor(req->wb_inode->i_dev),
+		dprintk("NFS: write (%s/%Ld %d@%Ld)",
+			req->wb_inode->i_sb->s_id,
 			(long long)NFS_FILEID(req->wb_inode),
 			req->wb_bytes,
 			(long long)(page_offset(page) + req->wb_offset));
@@ -1201,9 +1200,8 @@
 		req = nfs_list_entry(data->pages.next);
 		nfs_list_remove_request(req);
 
-		dprintk("NFS: commit (%02x:%02x/%Ld %d@%Ld)",
-			major(req->wb_inode->i_dev),
-			minor(req->wb_inode->i_dev),
+		dprintk("NFS: commit (%s/%Ld %d@%Ld)",
+			req->wb_inode->i_sb->s_id,
 			(long long)NFS_FILEID(req->wb_inode),
 			req->wb_bytes,
 			(long long)(page_offset(req->wb_page) + req->wb_offset));
diff -urN linux-2.5.2-pre7/fs/nfsd/vfs.c linux/fs/nfsd/vfs.c
--- linux-2.5.2-pre7/fs/nfsd/vfs.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/nfsd/vfs.c	Fri Jan  4 19:01:22 2002
@@ -1245,7 +1245,7 @@
 	tdir = tdentry->d_inode;
 
 	err = (rqstp->rq_vers == 2) ? nfserr_acces : nfserr_xdev;
-	if (!kdev_same(fdir->i_dev, tdir->i_dev))
+	if (fdir->i_sb != tdir->i_sb)
 		goto out;
 
 	err = nfserr_perm;
diff -urN linux-2.5.2-pre7/fs/ntfs/support.c linux/fs/ntfs/support.c
--- linux-2.5.2-pre7/fs/ntfs/support.c	Sun Dec 16 12:23:05 2001
+++ linux/fs/ntfs/support.c	Fri Jan  4 19:01:22 2002
@@ -200,7 +200,7 @@
 				if (buffer_req(bh) && !buffer_uptodate(bh)) {
 					printk(KERN_ERR "IO error syncing NTFS "
 					       "cluster [%s:%i]\n",
-					       bdevname(sb->s_dev), cluster);
+					       sb->s_id, cluster);
 					brelse(bh);
 					error = -EIO;
 					goto error_ret;
diff -urN linux-2.5.2-pre7/fs/partitions/acorn.c linux/fs/partitions/acorn.c
--- linux-2.5.2-pre7/fs/partitions/acorn.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/partitions/acorn.c	Fri Jan  4 19:01:22 2002
@@ -261,7 +261,7 @@
 	/*
 	 * Work out start of non-adfs partition.
 	 */
-	nr_sects = hd->part[MINOR(to_kdev_t(bdev->bd_dev))].nr_sects - start_sect;
+	nr_sects = hd->part[minor(to_kdev_t(bdev->bd_dev))].nr_sects - start_sect;
 
 	if (start_sect) {
 		first_sector += start_sect;
diff -urN linux-2.5.2-pre7/fs/qnx4/inode.c linux/fs/qnx4/inode.c
--- linux-2.5.2-pre7/fs/qnx4/inode.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/qnx4/inode.c	Fri Jan  4 19:01:22 2002
@@ -48,7 +48,7 @@
 		if (buffer_req(bh) && !buffer_uptodate(bh))
 		{
 			printk ("IO error syncing qnx4 inode [%s:%08lx]\n",
-				kdevname(inode->i_dev), inode->i_ino);
+				inode->i_sb->s_id, inode->i_ino);
 			err = -1;
 		}
 	        brelse (bh);
@@ -89,7 +89,7 @@
 	}
 	if (!ino) {
 		printk("qnx4: bad inode number on dev %s: %d is out of range\n",
-		       kdevname(inode->i_dev), ino);
+		       inode->i_sb->s_id, ino);
 		return;
 	}
 	QNX4DEBUG(("qnx4: write inode 2.\n"));
@@ -97,7 +97,7 @@
 	lock_kernel();
 	if (!(bh = sb_bread(inode->i_sb, block))) {
 		printk("qnx4: major problem: unable to read inode from dev "
-		       "%s\n", kdevname(inode->i_dev));
+		       "%s\n", inode->i_sb->s_id);
 		unlock_kernel();
 		return;
 	}
@@ -301,7 +301,7 @@
 	if (*(sb->u.qnx4_sb.sb->RootDir.di_fname) != '/') {
 		return "no qnx4 filesystem (no root dir).";
 	} else {
-		QNX4DEBUG(("QNX4 filesystem found on dev %s.\n", kdevname(sb->s_dev)));
+		QNX4DEBUG(("QNX4 filesystem found on dev %s.\n", sb->s_id));
 		rd = le32_to_cpu(sb->u.qnx4_sb.sb->RootDir.di_first_xtnt.xtnt_blk) - 1;
 		rl = le32_to_cpu(sb->u.qnx4_sb.sb->RootDir.di_first_xtnt.xtnt_size);
 		for (j = 0; j < rl; j++) {
@@ -440,6 +440,7 @@
 	struct buffer_head *bh;
 	struct qnx4_inode_entry *raw_inode;
 	int block, ino;
+	struct super_block *sb = inode->i_sb;
 
 	ino = inode->i_ino;
 	inode->i_mode = 0;
@@ -447,14 +448,14 @@
 	QNX4DEBUG(("Reading inode : [%d]\n", ino));
 	if (!ino) {
 		printk("qnx4: bad inode number on dev %s: %d is out of range\n",
-		       kdevname(inode->i_dev), ino);
+		       sb->s_id, ino);
 		return;
 	}
 	block = ino / QNX4_INODES_PER_BLOCK;
 
-	if (!(bh = sb_bread(inode->i_sb, block))) {
+	if (!(bh = sb_bread(sb, block))) {
 		printk("qnx4: major problem: unable to read inode from dev "
-		       "%s\n", kdevname(inode->i_dev));
+		       "%s\n", sb->s_id);
 		return;
 	}
 	raw_inode = ((struct qnx4_inode_entry *) bh->b_data) +
@@ -485,7 +486,7 @@
 		inode->i_mapping->a_ops = &qnx4_aops;
 		inode->u.qnx4_i.mmu_private = inode->i_size;
 	} else
-		printk("qnx4: bad inode %d on dev %s\n",ino,kdevname(inode->i_dev));
+		printk("qnx4: bad inode %d on dev %s\n",ino,sb->s_id);
 	brelse(bh);
 }
 
diff -urN linux-2.5.2-pre7/fs/qnx4/namei.c linux/fs/qnx4/namei.c
--- linux-2.5.2-pre7/fs/qnx4/namei.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/qnx4/namei.c	Fri Jan  4 19:01:22 2002
@@ -213,7 +213,7 @@
 	retval = -EPERM;
 	if (!inode->i_nlink) {
 		QNX4DEBUG(("Deleting nonexistent file (%s:%lu), %d\n",
-			   kdevname(inode->i_dev),
+			   inode->i_sb->s_id,
 			   inode->i_ino, inode->i_nlink));
 		inode->i_nlink = 1;
 	}
diff -urN linux-2.5.2-pre7/fs/reiserfs/bitmap.c linux/fs/reiserfs/bitmap.c
--- linux-2.5.2-pre7/fs/reiserfs/bitmap.c	Fri Nov  9 14:18:25 2001
+++ linux/fs/reiserfs/bitmap.c	Fri Jan  4 19:01:22 2002
@@ -111,8 +111,8 @@
   /* clear bit for the given block in bit map */
   if (!reiserfs_test_and_clear_le_bit (offset, apbh[nr]->b_data)) {
       reiserfs_warning ("vs-4080: reiserfs_free_block: "
-			"free_block (%04x:%lu)[dev:blocknr]: bit already cleared\n", 
-	    s->s_dev, block);
+			"free_block (%s:%lu)[dev:blocknr]: bit already cleared\n", 
+	    s->s_id, block);
   }
   journal_mark_dirty (th, s, apbh[nr]);
 
diff -urN linux-2.5.2-pre7/fs/reiserfs/fix_node.c linux/fs/reiserfs/fix_node.c
--- linux-2.5.2-pre7/fs/reiserfs/fix_node.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/reiserfs/fix_node.c	Fri Jan  4 19:01:22 2002
@@ -2115,7 +2115,7 @@
       reiserfs_panic (p_s_sb, "tb_buffer_sanity_check(): buffer is not in tree %s[%d] (%b)\n", descr, level, p_s_bh);
     }
 
-    if (p_s_bh->b_dev != p_s_sb->s_dev || 
+    if (!kdev_same(p_s_bh->b_dev, p_s_sb->s_dev) || 
 	p_s_bh->b_size != p_s_sb->s_blocksize ||
 	p_s_bh->b_blocknr > SB_BLOCK_COUNT(p_s_sb)) {
       reiserfs_panic (p_s_sb, "tb_buffer_sanity_check(): check failed for buffer %s[%d] (%b)\n", descr, level, p_s_bh);
diff -urN linux-2.5.2-pre7/fs/reiserfs/inode.c linux/fs/reiserfs/inode.c
--- linux-2.5.2-pre7/fs/reiserfs/inode.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/reiserfs/inode.c	Fri Jan  4 19:01:22 2002
@@ -965,7 +965,7 @@
     set_sd_v2_ctime(sd_v2, inode->i_ctime );
     set_sd_v2_blocks(sd_v2, inode->i_blocks );
     if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) {
-        set_sd_v2_rdev(sd_v2, inode->i_rdev );
+        set_sd_v2_rdev(sd_v2, kdev_t_to_nr(inode->i_rdev) );
 }
     else
     {
@@ -989,7 +989,7 @@
     set_sd_v1_mtime(sd_v1, inode->i_mtime );
 
     if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
-        set_sd_v1_rdev(sd_v1, inode->i_rdev );
+        set_sd_v1_rdev(sd_v1, kdev_t_to_nr(inode->i_rdev) );
     else
         set_sd_v1_blocks(sd_v1, inode->i_blocks );
 
@@ -1515,7 +1515,6 @@
     // these do not go to on-disk stat data
     inode->i_ino = le32_to_cpu (ih.ih_key.k_objectid);
     inode->i_blksize = PAGE_SIZE;
-    inode->i_dev = sb->s_dev;
   
     // store in in-core inode the key of stat data and version all
     // object items will have (directory items will have old offset
@@ -1789,7 +1788,7 @@
 	    goto research ;
 	}
     } else {
-        reiserfs_warning("clm-6003: bad item inode %lu, device %s\n", inode->i_ino, kdevname(inode->i_sb->s_dev)) ;
+        reiserfs_warning("clm-6003: bad item inode %lu, device %s\n", inode->i_ino, inode->i_sb->s_id) ;
         retval = -EIO ;
 	goto out ;
     }
diff -urN linux-2.5.2-pre7/fs/reiserfs/journal.c linux/fs/reiserfs/journal.c
--- linux-2.5.2-pre7/fs/reiserfs/journal.c	Sun Dec 16 12:23:05 2001
+++ linux/fs/reiserfs/journal.c	Fri Jan  4 19:01:22 2002
@@ -420,7 +420,7 @@
   struct reiserfs_journal_cnode *cn ;
   cn = journal_hash(table, dev, bl) ;
   while(cn) {
-    if ((cn->blocknr == bl) && (cn->dev == dev))
+    if ((cn->blocknr == bl) && (kdev_same(cn->dev, dev)))
       return cn ;
     cn = cn->hnext ;
   }
@@ -766,7 +766,7 @@
 
   cn = cn->hprev ;
   while(cn) {
-    if (cn->dev == dev && cn->blocknr == blocknr && cn->jlist) {
+    if (kdev_same(cn->dev, dev) && cn->blocknr == blocknr && cn->jlist) {
       return cn->jlist ;
     }
     cn = cn->hprev ;
@@ -1179,7 +1179,7 @@
                 mark_buffer_notjournal_dirty(cn->bh) ;
                 while(walk_cn) {
                     if (walk_cn->bh && walk_cn->blocknr == blocknr && 
-                         walk_cn->dev == cn->dev) {
+                         kdev_same(walk_cn->dev, cn->dev)) {
                         if (walk_cn->jlist) {
                             atomic_dec(&(walk_cn->jlist->j_nonzerolen)) ;
                         }
@@ -1282,7 +1282,7 @@
   }
   cur = *head ;
   while(cur) {
-    if (cur->blocknr == bh->b_blocknr && cur->dev == bh->b_dev && (jl == NULL || jl == cur->jlist) && 
+    if (cur->blocknr == bh->b_blocknr && kdev_same(cur->dev, bh->b_dev) && (jl == NULL || jl == cur->jlist) && 
         (!test_bit(BLOCK_FREED, &cur->state) || remove_freed)) {
       if (cur->hnext) {
         cur->hnext->hprev = cur->hprev ;
@@ -1293,7 +1293,7 @@
 	*head = cur->hnext ;
       }
       cur->blocknr = 0 ;
-      cur->dev = 0 ;
+      cur->dev = NODEV ;
       cur->state = 0 ;
       if (cur->bh && cur->jlist) /* anybody who clears the cur->bh will also dec the nonzerolen */
 	atomic_dec(&(cur->jlist->j_nonzerolen)) ;
@@ -1607,8 +1607,7 @@
   int ret ;
 
   cur_dblock = reiserfs_get_journal_block(p_s_sb) ;
-  printk("reiserfs: checking transaction log (device %s) ...\n",
-          kdevname(p_s_sb->s_dev)) ;
+  printk("reiserfs: checking transaction log (device %s) ...\n", p_s_sb->s_id) ;
   start = CURRENT_TIME ;
 
   /* step 1, read in the journal header block.  Check the transaction it says 
@@ -2302,7 +2301,7 @@
   ** to disk right now.
   */
   while(cur && can_dirty) {
-    if (cur->jlist && cur->bh && cur->blocknr && cur->dev == dev && 
+    if (cur->jlist && cur->bh && cur->blocknr && kdev_same(cur->dev, dev) && 
         cur->blocknr == blocknr) {
       can_dirty = 0 ;
     }
@@ -2315,7 +2314,7 @@
   while(cur && can_dirty) {
     if (cur->jlist && cur->jlist->j_len > 0 && 
         atomic_read(&(cur->jlist->j_commit_left)) > 0 && cur->bh && 
-        cur->blocknr && cur->dev == dev && cur->blocknr == blocknr) {
+        cur->blocknr && kdev_same(cur->dev, dev) && cur->blocknr == blocknr) {
       can_dirty = 0 ;
     }
     cur = cur->hnext ;
@@ -2582,7 +2581,7 @@
     /* find all older transactions with this block, make sure they don't try to write it out */
     cn = get_journal_hash_dev(SB_JOURNAL(p_s_sb)->j_list_hash_table, p_s_sb->s_dev, blocknr, p_s_sb->s_blocksize) ;
     while (cn) {
-      if (p_s_sb->s_dev == cn->dev && blocknr == cn->blocknr) {
+      if (kdev_same(p_s_sb->s_dev, cn->dev) && blocknr == cn->blocknr) {
 	set_bit(BLOCK_FREED, &cn->state) ;
 	if (cn->bh) {
 	  if (!cleaned) {
diff -urN linux-2.5.2-pre7/fs/reiserfs/namei.c linux/fs/reiserfs/namei.c
--- linux-2.5.2-pre7/fs/reiserfs/namei.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/reiserfs/namei.c	Fri Jan  4 19:01:22 2002
@@ -817,7 +817,7 @@
   
     if (!inode->i_nlink) {
 	printk("reiserfs_unlink: deleting nonexistent file (%s:%lu), %d\n",
-	       kdevname(inode->i_dev), inode->i_ino, inode->i_nlink);
+	       inode->i_sb->s_id, inode->i_ino, inode->i_nlink);
 	inode->i_nlink = 1;
     }
 
diff -urN linux-2.5.2-pre7/fs/reiserfs/prints.c linux/fs/reiserfs/prints.c
--- linux-2.5.2-pre7/fs/reiserfs/prints.c	Fri Nov  9 14:18:25 2001
+++ linux/fs/reiserfs/prints.c	Fri Jan  4 19:01:22 2002
@@ -335,7 +335,7 @@
 
   /* this is not actually called, but makes reiserfs_panic() "noreturn" */
   panic ("REISERFS: panic (device %s): %s\n",
-	 sb ? kdevname(sb->s_dev) : "sb == 0", error_buf);
+	 sb ? sb->s_id : "sb == 0", error_buf);
 }
 
 
diff -urN linux-2.5.2-pre7/fs/reiserfs/procfs.c linux/fs/reiserfs/procfs.c
--- linux-2.5.2-pre7/fs/reiserfs/procfs.c	Fri Nov  9 14:18:25 2001
+++ linux/fs/reiserfs/procfs.c	Fri Jan  4 19:01:22 2002
@@ -546,14 +546,13 @@
 int reiserfs_proc_info_init( struct super_block *sb )
 {
 	spin_lock_init( & __PINFO( sb ).lock );
-	sb->u.reiserfs_sb.procdir = proc_mkdir( bdevname( sb -> s_dev ), 
-						proc_info_root );
+	sb->u.reiserfs_sb.procdir = proc_mkdir(sb->s_id, proc_info_root);
 	if( sb->u.reiserfs_sb.procdir ) {
 		sb->u.reiserfs_sb.procdir -> owner = THIS_MODULE;
 		return 0;
 	}
 	reiserfs_warning( "reiserfs: cannot create /proc/%s/%s\n",
-			  proc_info_root_name, bdevname( sb -> s_dev ) );
+			  proc_info_root_name, sb->s_id );
 	return 1;
 }
 
@@ -564,7 +563,7 @@
 	__PINFO( sb ).exiting = 1;
 	spin_unlock( & __PINFO( sb ).lock );
 	if ( proc_info_root ) {
-		remove_proc_entry( bdevname( sb -> s_dev ), proc_info_root );
+		remove_proc_entry( sb->s_id, proc_info_root );
 		sb->u.reiserfs_sb.procdir = NULL;
 	}
 	return 0;
diff -urN linux-2.5.2-pre7/fs/reiserfs/stree.c linux/fs/reiserfs/stree.c
--- linux-2.5.2-pre7/fs/reiserfs/stree.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/reiserfs/stree.c	Fri Jan  4 19:01:22 2002
@@ -395,7 +395,7 @@
 	  p_s_chk_path->path_length > MAX_HEIGHT,
 	  "PAP-5050: pointer to the key(%p) is NULL or illegal path length(%d)",
 	  p_s_key, p_s_chk_path->path_length);
-  RFALSE( PATH_PLAST_BUFFER(p_s_chk_path)->b_dev == NODEV,
+  RFALSE( kdev_same(PATH_PLAST_BUFFER(p_s_chk_path)->b_dev, NODEV),
 	  "PAP-5060: device must not be NODEV");
 
   if ( COMP_KEYS(get_lkey(p_s_chk_path, p_s_sb), p_s_key) == 1 )
diff -urN linux-2.5.2-pre7/fs/reiserfs/super.c linux/fs/reiserfs/super.c
--- linux-2.5.2-pre7/fs/reiserfs/super.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/reiserfs/super.c	Fri Jan  4 19:01:22 2002
@@ -221,7 +221,7 @@
 
 
 int reiserfs_is_super(struct super_block *s) {
-   return (s->s_dev != 0 && s->s_op == &reiserfs_sops) ;
+   return (!kdev_same(s->s_dev, NODEV) && s->s_op == &reiserfs_sops) ;
 }
 
 
@@ -366,7 +366,7 @@
     if (!bh) {
       printk ("read_super_block: "
               "bread failed (dev %s, block %d, size %d)\n",
-              kdevname (s->s_dev), offset / s->s_blocksize, s->s_blocksize);
+              s->s_id, offset / s->s_blocksize, s->s_blocksize);
       return 1;
     }
  
@@ -374,7 +374,7 @@
     if (!is_reiserfs_magic_string (rs)) {
       printk ("read_super_block: "
               "can't find a reiserfs filesystem on (dev %s, block %lu, size %d)\n",
-              kdevname(s->s_dev), bh->b_blocknr, s->s_blocksize);
+              s->s_id, bh->b_blocknr, s->s_blocksize);
       brelse (bh);
       return 1;
     }
@@ -390,7 +390,7 @@
     if (!bh) {
 	printk("read_super_block: "
                 "bread failed (dev %s, block %d, size %d)\n",
-                kdevname (s->s_dev), offset / s->s_blocksize, s->s_blocksize);
+                s->s_id, offset / s->s_blocksize, s->s_blocksize);
 	return 1;
     }
     
@@ -398,9 +398,9 @@
     if (!is_reiserfs_magic_string (rs) || sb_blocksize(rs) != s->s_blocksize) {
 	printk ("read_super_block: "
 		"can't find a reiserfs filesystem on (dev %s, block %lu, size %d)\n",
-		kdevname(s->s_dev), bh->b_blocknr, s->s_blocksize);
+		s->s_id, bh->b_blocknr, s->s_blocksize);
 	brelse (bh);
-	printk ("read_super_block: can't find a reiserfs filesystem on dev %s.\n", kdevname(s->s_dev));
+	printk ("read_super_block: can't find a reiserfs filesystem on dev %s.\n", s->s_id);
 	return 1;
     }
     /* must check to be sure we haven't pulled an old format super out
diff -urN linux-2.5.2-pre7/fs/romfs/inode.c linux/fs/romfs/inode.c
--- linux-2.5.2-pre7/fs/romfs/inode.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/romfs/inode.c	Fri Jan  4 19:01:22 2002
@@ -96,7 +96,6 @@
 romfs_read_super(struct super_block *s, void *data, int silent)
 {
 	struct buffer_head *bh;
-	kdev_t dev = s->s_dev;
 	struct romfs_super_block *rsb;
 	int sz;
 
@@ -119,12 +118,12 @@
 	   || sz < ROMFH_SIZE) {
 		if (!silent)
 			printk ("VFS: Can't find a romfs filesystem on dev "
-				"%s.\n", kdevname(dev));
+				"%s.\n", s->s_id);
 		goto out;
 	}
 	if (romfs_checksum(rsb, min_t(int, sz, 512))) {
 		printk ("romfs: bad initial checksum on dev "
-			"%s.\n", kdevname(dev));
+			"%s.\n", s->s_id);
 		goto out;
 	}
 
diff -urN linux-2.5.2-pre7/fs/super.c linux/fs/super.c
--- linux-2.5.2-pre7/fs/super.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/super.c	Fri Jan  4 19:01:22 2002
@@ -718,16 +718,17 @@
 	s->s_bdev = bdev;
 	s->s_flags = flags;
 	insert_super(s, fs_type);
+	strncpy(s->s_id, bdevname(dev), sizeof(s->s_id));
+	error = -EINVAL;
 	if (!fs_type->read_super(s, data, flags & MS_VERBOSE ? 1 : 0))
-		goto Einval;
+		goto failed;
 	s->s_flags |= MS_ACTIVE;
 	path_release(&nd);
 	return s;
 
-Einval:
+failed:
 	deactivate_super(s);
 	remove_super(s);
-	error = -EINVAL;
 	goto out;
 out1:
 	blkdev_put(bdev, BDEV_FS);
diff -urN linux-2.5.2-pre7/fs/sysv/ialloc.c linux/fs/sysv/ialloc.c
--- linux-2.5.2-pre7/fs/sysv/ialloc.c	Sun Dec 16 12:23:05 2001
+++ linux/fs/sysv/ialloc.c	Fri Jan  4 19:01:22 2002
@@ -109,7 +109,7 @@
 	clear_inode(inode);
 	if (!raw_inode) {
 		printk("sysv_free_inode: unable to read inode block on device "
-		       "%s\n", bdevname(inode->i_dev));
+		       "%s\n", inode->i_sb->s_id);
 		return;
 	}
 	lock_super(sb);
diff -urN linux-2.5.2-pre7/fs/sysv/inode.c linux/fs/sysv/inode.c
--- linux-2.5.2-pre7/fs/sysv/inode.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/sysv/inode.c	Fri Jan  4 19:01:22 2002
@@ -149,15 +149,14 @@
 
 	ino = inode->i_ino;
 	if (!ino || ino > sb->sv_ninodes) {
-		printk("Bad inode number on dev %s"
-		       ": %d is out of range\n",
-		       kdevname(inode->i_dev), ino);
+		printk("Bad inode number on dev %s: %d is out of range\n",
+		       inode->i_sb->s_id, ino);
 		goto bad_inode;
 	}
 	raw_inode = sysv_raw_inode(sb, ino, &bh);
 	if (!raw_inode) {
 		printk("Major problem: unable to read inode from dev %s\n",
-		       bdevname(inode->i_dev));
+		       inode->i_sb->s_id);
 		goto bad_inode;
 	}
 	/* SystemV FS: kludge permissions if ino==SYSV_ROOT_INO ?? */
@@ -195,7 +194,7 @@
 	ino = inode->i_ino;
 	if (!ino || ino > sb->sv_ninodes) {
 		printk("Bad inode number on dev %s: %d is out of range\n",
-		       bdevname(inode->i_dev), ino);
+		       inode->i_sb->s_id, ino);
 		return 0;
 	}
 	raw_inode = sysv_raw_inode(sb, ino, &bh);
@@ -242,7 +241,7 @@
                 wait_on_buffer(bh);
                 if (buffer_req(bh) && !buffer_uptodate(bh)) {
                         printk ("IO error syncing sysv inode [%s:%08lx]\n",
-                                bdevname(inode->i_dev), inode->i_ino);
+                                inode->i_sb->s_id, inode->i_ino);
                         err = -1;
                 }
         }
diff -urN linux-2.5.2-pre7/fs/sysv/super.c linux/fs/sysv/super.c
--- linux-2.5.2-pre7/fs/sysv/super.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/sysv/super.c	Fri Jan  4 19:01:22 2002
@@ -208,7 +208,7 @@
  		if (!(sb->s_flags & MS_RDONLY)) {
  			printk("SysV FS: SCO EAFS on %s detected, " 
  				"forcing read-only mode.\n", 
- 				bdevname(sb->s_dev));
+ 				sb->s_id);
  			sb->s_flags |= MS_RDONLY;
  		}
  		return sbd->s_type;
@@ -232,7 +232,7 @@
 
 	if (sbd->s_type >= 0x10) {
 		printk("SysV FS: can't handle long file names on %s, "
-		       "forcing read-only mode.\n", kdevname(sb->s_dev));
+		       "forcing read-only mode.\n", sb->s_id);
 		sb->s_flags |= MS_RDONLY;
 	}
 
@@ -317,7 +317,7 @@
 
 	if (!silent)
 		printk("VFS: Found a %s FS (block size = %ld) on device %s\n",
-		       found, sb->s_blocksize, bdevname(sb->s_dev));
+		       found, sb->s_blocksize, sb->s_id);
 
 	sb->s_magic = SYSV_MAGIC_BASE + sb->sv_type;
 	/* set up enough so that it can read an inode */
@@ -345,7 +345,6 @@
 {
 	struct buffer_head *bh1;
 	struct buffer_head *bh = NULL;
-	kdev_t dev = sb->s_dev;
 	unsigned long blocknr;
 	int size = 0;
 	int i;
@@ -412,7 +411,7 @@
 	brelse(bh);
 	if (!silent)
 		printk("VFS: unable to find oldfs superblock on device %s\n",
-			bdevname(dev));
+			sb->s_id);
 	goto failed;
 Ebadsize:
 	brelse(bh);
@@ -426,7 +425,6 @@
 				  int silent)
 {
 	struct buffer_head *bh, *bh2 = NULL;
-	kdev_t dev = sb->s_dev;
 	struct v7_super_block *v7sb;
 	struct sysv_inode *v7i;
 
@@ -443,7 +441,7 @@
 	if ((bh = sb_bread(sb, 1)) == NULL) {
 		if (!silent)
 			printk("VFS: unable to read V7 FS superblock on "
-			       "device %s.\n", bdevname(dev));
+			       "device %s.\n", sb->s_id);
 		goto failed;
 	}
 
diff -urN linux-2.5.2-pre7/fs/udf/inode.c linux/fs/udf/inode.c
--- linux-2.5.2-pre7/fs/udf/inode.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/udf/inode.c	Fri Jan  4 19:01:22 2002
@@ -1531,7 +1531,7 @@
 		if (buffer_req(bh) && !buffer_uptodate(bh))
 		{
 			printk("IO error syncing udf inode [%s:%08lx]\n",
-				bdevname(inode->i_dev), inode->i_ino);
+				inode->i_sb->s_id, inode->i_ino);
 			err = -EIO;
 		}
 	}
diff -urN linux-2.5.2-pre7/fs/udf/super.c linux/fs/udf/super.c
--- linux-2.5.2-pre7/fs/udf/super.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/udf/super.c	Fri Jan  4 19:01:22 2002
@@ -1549,7 +1549,7 @@
 	vsprintf(error_buf, fmt, args);
 	va_end(args);
 	printk (KERN_CRIT "UDF-fs error (device %s): %s: %s\n",
-		bdevname(sb->s_dev), function, error_buf);
+		sb->s_id, function, error_buf);
 }
 
 void udf_warning(struct super_block *sb, const char *function,
@@ -1561,7 +1561,7 @@
 	vsprintf(error_buf, fmt, args);
 	va_end(args);
 	printk(KERN_WARNING "UDF-fs warning (device %s): %s: %s\n",
-		bdevname(sb->s_dev), function, error_buf);
+		sb->s_id, function, error_buf);
 }
 
 /*
diff -urN linux-2.5.2-pre7/fs/ufs/super.c linux/fs/ufs/super.c
--- linux-2.5.2-pre7/fs/ufs/super.c	Fri Jan  4 19:01:17 2002
+++ linux/fs/ufs/super.c	Fri Jan  4 19:01:22 2002
@@ -203,13 +203,13 @@
 	switch (sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_ONERROR) {
 	case UFS_MOUNT_ONERROR_PANIC:
 		panic ("UFS-fs panic (device %s): %s: %s\n", 
-			kdevname(sb->s_dev), function, error_buf);
+			sb->s_id, function, error_buf);
 
 	case UFS_MOUNT_ONERROR_LOCK:
 	case UFS_MOUNT_ONERROR_UMOUNT:
 	case UFS_MOUNT_ONERROR_REPAIR:
 		printk (KERN_CRIT "UFS-fs error (device %s): %s: %s\n",
-			kdevname(sb->s_dev), function, error_buf);
+			sb->s_id, function, error_buf);
 	}		
 }
 
@@ -233,7 +233,7 @@
 	va_end (args);
 	sb->s_flags |= MS_RDONLY;
 	printk (KERN_CRIT "UFS-fs panic (device %s): %s: %s\n",
-		kdevname(sb->s_dev), function, error_buf);
+		sb->s_id, function, error_buf);
 }
 
 void ufs_warning (struct super_block * sb, const char * function,
@@ -245,7 +245,7 @@
 	vsprintf (error_buf, fmt, args);
 	va_end (args);
 	printk (KERN_WARNING "UFS-fs warning (device %s): %s: %s\n",
-		kdevname(sb->s_dev), function, error_buf);
+		sb->s_id, function, error_buf);
 }
 
 static int ufs_parse_options (char * options, unsigned * mount_options)
diff -urN linux-2.5.2-pre7/include/linux/fs.h linux/include/linux/fs.h
--- linux-2.5.2-pre7/include/linux/fs.h	Fri Jan  4 19:01:17 2002
+++ linux/include/linux/fs.h	Fri Jan  4 19:01:22 2002
@@ -718,6 +718,8 @@
 	struct list_head	s_instances;
 	struct quota_mount_options s_dquot;	/* Diskquota specific options */
 
+	char s_id[32];				/* Informational name */
+
 	union {
 		struct minix_sb_info	minix_sb;
 		struct ext2_sb_info	ext2_sb;
diff -urN linux-2.5.2-pre7/include/linux/hfs_sysdep.h linux/include/linux/hfs_sysdep.h
--- linux-2.5.2-pre7/include/linux/hfs_sysdep.h	Tue Feb 13 14:13:44 2001
+++ linux/include/linux/hfs_sysdep.h	Fri Jan  4 19:01:22 2002
@@ -122,7 +122,7 @@
 }
 
 static inline const char *hfs_mdb_name(hfs_sysmdb sys_mdb) {
-	return kdevname(sys_mdb->s_dev);
+	return sys_mdb->s_id;
 }
 
 
diff -urN linux-2.5.2-pre7/include/linux/reiserfs_fs.h linux/include/linux/reiserfs_fs.h
--- linux-2.5.2-pre7/include/linux/reiserfs_fs.h	Fri Jan  4 19:01:17 2002
+++ linux/include/linux/reiserfs_fs.h	Fri Jan  4 19:01:22 2002
@@ -1651,7 +1651,7 @@
 #define _jhashfn(dev,block)	\
 	((((dev)<<(JBH_HASH_SHIFT - 6)) ^ ((dev)<<(JBH_HASH_SHIFT - 9))) ^ \
 	 (((block)<<(JBH_HASH_SHIFT - 6)) ^ ((block) >> 13) ^ ((block) << (JBH_HASH_SHIFT - 12))))
-#define journal_hash(t,dev,block) ((t)[_jhashfn((dev),(block)) & JBH_HASH_MASK])
+#define journal_hash(t,dev,block) ((t)[_jhashfn((kdev_t_to_nr(dev)),(block)) & JBH_HASH_MASK])
 
 /* finds n'th buffer with 0 being the start of this commit.  Needs to go away, j_ap_blocks has changed
 ** since I created this.  One chunk of code in journal.c needs changing before deleting it
diff -urN linux-2.5.2-pre7/include/linux/usb.h linux/include/linux/usb.h
--- linux-2.5.2-pre7/include/linux/usb.h	Fri Jan  4 19:01:17 2002
+++ linux/include/linux/usb.h	Fri Jan  4 19:01:22 2002
@@ -460,6 +460,7 @@
 
 /**
  * struct usb_driver - identifies USB driver to usbcore
+ * @owner: pointer to the module owner of this driver
  * @name: The driver name should be unique among USB drivers
  * @probe: Called to see if the driver is willing to manage a particular
  *	interface on a device.  The probe routine returns a handle that 
@@ -502,6 +503,7 @@
  * well as cancel any I/O requests that are still pending.
  */
 struct usb_driver {
+	struct module *owner;
 	const char *name;
 
 	void *(*probe)(
diff -urN linux-2.5.2-pre7/init/do_mounts.c linux/init/do_mounts.c
--- linux-2.5.2-pre7/init/do_mounts.c	Fri Jan  4 19:01:17 2002
+++ linux/init/do_mounts.c	Fri Jan  4 19:01:22 2002
@@ -631,7 +631,7 @@
 #ifdef CONFIG_BLK_DEV_RAM
 	if (rd_prompt)
 		change_floppy("root floppy disk to be loaded into RAM disk");
-	create_dev("/dev/ram", MKDEV(RAMDISK_MAJOR, n), NULL);
+	create_dev("/dev/ram", mk_kdev(RAMDISK_MAJOR, n), NULL);
 #endif
 	return rd_load_image("/dev/root");
 }
@@ -696,7 +696,7 @@
 static void __init mount_root(void)
 {
 #ifdef CONFIG_ROOT_NFS
-	if (MAJOR(ROOT_DEV) == UNNAMED_MAJOR) {
+	if (major(ROOT_DEV) == UNNAMED_MAJOR) {
 		if (mount_nfs_root()) {
 			sys_chdir("/root");
 			ROOT_DEV = current->fs->pwdmnt->mnt_sb->s_dev;
@@ -704,7 +704,7 @@
 			return;
 		}
 		printk(KERN_ERR "VFS: Unable to mount root fs via NFS, trying floppy.\n");
-		ROOT_DEV = MKDEV(FLOPPY_MAJOR, 0);
+		ROOT_DEV = mk_kdev(FLOPPY_MAJOR, 0);
 	}
 #endif
 	devfs_make_root(root_device_name);
@@ -749,7 +749,7 @@
 static void __init handle_initrd(void)
 {
 #ifdef CONFIG_BLK_DEV_INITRD
-	int ram0 = kdev_t_to_nr(MKDEV(RAMDISK_MAJOR,0));
+	kdev_t ram0 = mk_kdev(RAMDISK_MAJOR,0);
 	int error;
 	int i, pid;
 
@@ -769,12 +769,12 @@
 	sys_mount("..", ".", NULL, MS_MOVE, NULL);
 	sys_umount("/old/dev", 0);
 
-	if (real_root_dev == ram0) {
+	if (real_root_dev == kdev_t_to_nr(ram0)) {
 		sys_chdir("/old");
 		return;
 	}
 
-	ROOT_DEV = real_root_dev;
+	ROOT_DEV = to_kdev_t(real_root_dev);
 	mount_root();
 
 	printk(KERN_NOTICE "Trying to move old root to /initrd ... ");
@@ -801,8 +801,8 @@
 static int __init initrd_load(void)
 {
 #ifdef CONFIG_BLK_DEV_INITRD
-	create_dev("/dev/ram", MKDEV(RAMDISK_MAJOR, 0), NULL);
-	create_dev("/dev/initrd", MKDEV(RAMDISK_MAJOR, INITRD_MINOR), NULL);
+	create_dev("/dev/ram", mk_kdev(RAMDISK_MAJOR, 0), NULL);
+	create_dev("/dev/initrd", mk_kdev(RAMDISK_MAJOR, INITRD_MINOR), NULL);
 #endif
 	return rd_load_image("/dev/initrd");
 }
@@ -816,7 +816,7 @@
 #ifdef CONFIG_BLK_DEV_INITRD
 	if (!initrd_start)
 		mount_initrd = 0;
-	real_root_dev = ROOT_DEV;
+	real_root_dev = kdev_t_to_nr(ROOT_DEV);
 #endif
 	sys_mkdir("/dev", 0700);
 	sys_mkdir("/root", 0700);
diff -urN linux-2.5.2-pre7/kernel/sched.c linux/kernel/sched.c
--- linux-2.5.2-pre7/kernel/sched.c	Fri Jan  4 19:01:17 2002
+++ linux/kernel/sched.c	Fri Jan  4 19:01:22 2002
@@ -54,8 +54,8 @@
  * task. The default time slice for zero-nice tasks will be 37ms.
  */
 #define NICE_RANGE	40
-#define MIN_NICE_TSLICE	5000
-#define MAX_NICE_TSLICE	70000
+#define MIN_NICE_TSLICE	10000
+#define MAX_NICE_TSLICE	90000
 #define TASK_TIMESLICE(p)	((int) ts_table[19 - (p)->nice])
 
 static unsigned char ts_table[NICE_RANGE];
@@ -538,17 +538,11 @@
 		goto need_resched;
 
 	if (!--p->time_slice) {
-		if (p->dyn_prio) {
-			p->time_slice--;
+		if (p->dyn_prio)
 			p->dyn_prio--;
-		}
-		p->need_resched = 1;
-	} else
-		if (p->time_slice < -TASK_TIMESLICE(p)) {
-			p->time_slice = 0;
 need_resched:
-			p->need_resched = 1;
-		}
+		p->need_resched = 1;
+	}
 }
 
 /*
diff -urN linux-2.5.2-pre7/net/ipv4/ipconfig.c linux/net/ipv4/ipconfig.c
--- linux-2.5.2-pre7/net/ipv4/ipconfig.c	Thu Nov 29 07:49:10 2001
+++ linux/net/ipv4/ipconfig.c	Fri Jan  4 19:01:22 2002
@@ -1145,7 +1145,7 @@
 	 */
 	if (ic_myaddr == INADDR_NONE ||
 #ifdef CONFIG_ROOT_NFS
-	    (MAJOR(ROOT_DEV) == UNNAMED_MAJOR
+	    (major(ROOT_DEV) == UNNAMED_MAJOR
 	     && root_server_addr == INADDR_NONE
 	     && ic_servaddr == INADDR_NONE) ||
 #endif
@@ -1170,7 +1170,7 @@
 			 * 				-- Chip
 			 */
 #ifdef CONFIG_ROOT_NFS
-			if (ROOT_DEV == MKDEV(UNNAMED_MAJOR, 255)) {
+			if (kdev_same(ROOT_DEV, mk_kdev(UNNAMED_MAJOR, 255))) {
 				printk(KERN_ERR 
 					"IP-Config: Retrying forever (NFS root)...\n");
 				goto try_try_again;
