]> git.neil.brown.name Git - history.git/commitdiff
v2.5.1.7 -> v2.5.1.8
authorLinus Torvalds <torvalds@athlon.transmeta.com>
Tue, 5 Feb 2002 07:59:55 +0000 (23:59 -0800)
committerLinus Torvalds <torvalds@athlon.transmeta.com>
Tue, 5 Feb 2002 07:59:55 +0000 (23:59 -0800)
- Greg KH: USB updates
- various: kdev_t updates
- Al Viro: more bread()/filesystem cleanups

115 files changed:
Documentation/usb/ov511.txt
Makefile
arch/alpha/kernel/pci-noop.c
arch/alpha/kernel/pci_iommu.c
arch/alpha/lib/dec_and_lock.c
arch/i386/defconfig
drivers/block/nbd.c
drivers/block/paride/pcd.c
drivers/block/paride/pd.c
drivers/block/paride/pf.c
drivers/block/paride/pg.c
drivers/block/paride/pt.c
drivers/block/rd.c
drivers/block/xd.c
drivers/cdrom/aztcd.c
drivers/cdrom/cdu31a.c
drivers/cdrom/cm206.c
drivers/cdrom/gscd.c
drivers/cdrom/mcd.c
drivers/cdrom/mcdx.c
drivers/cdrom/optcd.c
drivers/cdrom/sjcd.c
drivers/char/serial.c
drivers/scsi/st.c
drivers/sound/ymfpci.c
drivers/usb/catc.c
drivers/usb/devio.c
drivers/usb/ov511.c
drivers/usb/ov511.h
drivers/usb/pwc-ctrl.c
drivers/usb/pwc-if.c
drivers/usb/pwc-ioctl.h
drivers/usb/pwc.h
drivers/usb/serial/usb-serial.h
drivers/usb/serial/usbserial.c
drivers/usb/serial/visor.c
drivers/usb/storage/datafab.c
drivers/usb/storage/freecom.c
drivers/usb/storage/jumpshot.c
drivers/usb/storage/sddr09.c
drivers/usb/storage/shuttle_usbat.c
drivers/usb/storage/transport.c
drivers/usb/storage/unusual_devs.h
drivers/usb/usb.c
drivers/usb/usbnet.c
fs/adfs/super.c
fs/affs/amigaffs.c
fs/affs/bitmap.c
fs/affs/super.c
fs/bfs/dir.c
fs/bfs/inode.c
fs/coda/psdev.c
fs/devfs/base.c
fs/devfs/util.c
fs/ext2/inode.c
fs/ext2/super.c
fs/ext3/ialloc.c
fs/ext3/super.c
fs/fat/cache.c
fs/fat/inode.c
fs/fat/misc.c
fs/freevxfs/vxfs.h
fs/freevxfs/vxfs_bmap.c
fs/freevxfs/vxfs_extern.h
fs/freevxfs/vxfs_fshead.c
fs/freevxfs/vxfs_inode.c
fs/freevxfs/vxfs_kcompat.h [new file with mode: 0644]
fs/freevxfs/vxfs_lookup.c
fs/freevxfs/vxfs_olt.c
fs/freevxfs/vxfs_subr.c
fs/freevxfs/vxfs_super.c
fs/hfs/inode.c
fs/hfs/super.c
fs/hpfs/super.c
fs/isofs/inode.c
fs/jbd/journal.c
fs/jffs/inode-v23.c
fs/jffs2/super.c
fs/minix/bitmap.c
fs/minix/inode.c
fs/nfs/dir.c
fs/nfs/file.c
fs/nfs/inode.c
fs/nfs/nfsroot.c
fs/nfs/read.c
fs/nfs/write.c
fs/nfsd/vfs.c
fs/ntfs/support.c
fs/partitions/acorn.c
fs/qnx4/inode.c
fs/qnx4/namei.c
fs/reiserfs/bitmap.c
fs/reiserfs/fix_node.c
fs/reiserfs/inode.c
fs/reiserfs/journal.c
fs/reiserfs/namei.c
fs/reiserfs/prints.c
fs/reiserfs/procfs.c
fs/reiserfs/stree.c
fs/reiserfs/super.c
fs/romfs/inode.c
fs/super.c
fs/sysv/ialloc.c
fs/sysv/inode.c
fs/sysv/super.c
fs/udf/inode.c
fs/udf/super.c
fs/ufs/super.c
include/linux/fs.h
include/linux/hfs_sysdep.h
include/linux/reiserfs_fs.h
include/linux/usb.h
init/do_mounts.c
kernel/sched.c
net/ipv4/ipconfig.c

index bc9cad2abb54d7922a3961b29db30965be73e9f4..519bcf8bcd9ecc8cf1cfe4ee2931340adce6ec6d 100644 (file)
@@ -8,11 +8,11 @@ Homepage: http://alpha.dyndns.org/ov511
 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 @@ WHAT YOU NEED:
 
 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 @@ at 640x480.
        
 [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 @@ MODULE PARAMETERS:
           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.
-
-  NAME: force_rgb
-  TYPE: integer (boolean)
+  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      (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 @@ MODULE PARAMETERS:
         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 @@ Randy Dunlap, and others. Big thanks to them for their pioneering work on that
 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.
+
index 35a9e12be6d8d28d0e0c362fdeba533c76452ccf..debe2bd84c426c16a20f5e873a774a8a96e091cc 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 5
 SUBLEVEL = 2
-EXTRAVERSION =-pre7
+EXTRAVERSION =-pre8
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
index 496b92ca15473c2793f3aee13592c6c6edea5226..860875f555e5a82b231a608d59c7eb0bd19bb90c 100644 (file)
@@ -126,6 +126,7 @@ int
 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,
index ffa69a431b5703e7e18aa89f605dda0dc0941b96..eb2bf74d8b1ca29b57aa5578d9df767c8a704439 100644 (file)
@@ -215,12 +215,20 @@ pci_map_single_1(struct pci_dev *pdev, void *cpu_addr, size_t size,
        /* 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 @@ pci_map_single_1(struct pci_dev *pdev, void *cpu_addr, size_t size,
 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 @@ pci_map_sg(struct pci_dev *pdev, struct scatterlist *sg, int nents,
        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 @@ pci_map_sg(struct pci_dev *pdev, struct scatterlist *sg, int nents,
                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 @@ pci_unmap_sg(struct pci_dev *pdev, struct scatterlist *sg, int nents,
        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 @@ pci_dma_supported(struct pci_dev *pdev, u64 mask)
        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;
 
index 875966988f84465bd8d970ed0eb67ae4f2c74dfd..98598bc0ed2be0115cbe27a32325a86bce6b29e6 100644 (file)
@@ -27,6 +27,7 @@ atomic_dec_and_lock:                          \n\
        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))
index 49e258065ad50fd9661e196633c040c80b864ad8..88b531035188133bbfe611f6d892c6cc97c773ad 100644 (file)
@@ -728,8 +728,9 @@ CONFIG_USB=y
 # 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_STORAGE=y
 # 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_STORAGE=y
 # 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
index b327d5786ba26511f22319bad71d5e061f3ab56d..49b2e7dc1d5b02e24eb63628c19499bab1c8ef57 100644 (file)
@@ -78,7 +78,7 @@ static int nbd_open(struct inode *inode, struct file *file)
 
        if (!inode)
                return -EINVAL;
-       dev = MINOR(inode->i_rdev);
+       dev = minor(inode->i_rdev);
        if (dev >= MAX_NBD)
                return -ENODEV;
 
@@ -253,7 +253,7 @@ void nbd_do_it(struct nbd_device *lo)
                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 @@ void nbd_clear_que(struct nbd_device *lo)
                        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 @@ static void do_nbd_request(request_queue_t * q)
                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 @@ static int nbd_ioctl(struct inode *inode, struct file *file,
                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 @@ static int nbd_release(struct inode *inode, struct file *file)
 
        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 @@ static int __init nbd_init(void)
                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);
index 9604464e325f89facb9883c708f0af91a47f3ac4..75e6f694be28ba465529ab7d40d998a036ce2e45 100644 (file)
@@ -182,7 +182,7 @@ MODULE_PARM(drive3,"1-6i");
 #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 @@ static void pcd_init_units( void )
 
                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 @@ static void do_pcd_request (request_queue_t * q)
            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;
index 56499c8dd335648d07812ac1645df3941f738589..dbf1c91f2cacf370783f68972ae4c8eb9d05f555 100644 (file)
@@ -206,7 +206,7 @@ MODULE_PARM(drive3,"1-8i");
 #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 @@ static int pd_ioctl(struct inode *inode,struct file *file,
        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 @@ static int pd_detect( void )
                 } 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 @@ repeat:
         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 @@ static void pd_next_buf( int unit )
 
        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",
index c83901c70d366811f52cff658de76ef288ffcfae..0088d79387a18f889b96612fe2e94eba5f83fda8 100644 (file)
@@ -202,7 +202,7 @@ MODULE_PARM(drive3,"1-7i");
 #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 @@ int pf_init (void)      /* preliminary initialisation */
        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 @@ static int pf_ioctl(struct inode *inode,struct file *file,
 {       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;
index bf680c0229e29feca729625f951034abff07d1a8..bf98ec1de86e1a541356d57ec7e92750c2d0d811 100644 (file)
@@ -565,7 +565,7 @@ static int pg_detect( void )
        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)
 
index 725960050544413aa32ce5d2d1f77f2cbcfcf247..9e9e59238656cf44e982d5b7d4eb8331b59bf543 100644 (file)
@@ -688,7 +688,7 @@ static int pt_detect( void )
        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 @@ static int pt_open (struct inode *inode, struct file *file)
                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 @@ static int pt_ioctl(struct inode *inode,struct file *file,
        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)
index 25f0ebe2fc4fdfdd6f7d3ae186ca28c1bcd33ba5..c2c352d4d24b07d951daf69e7a603d9c432ee686 100644 (file)
@@ -453,7 +453,7 @@ static int __init rd_init (void)
 
 #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
index 30aa2ee030a21a22357be841653429b45cd5cfb2..9cdaf6d7e8df1650ea1d1bb7574ee90338859de5 100644 (file)
@@ -249,7 +249,8 @@ static void __init xd_geninit (void)
 
        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 @@ static void do_xd_request (request_queue_t * q)
                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 @@ static int xd_ioctl (struct inode *inode,struct file *file,u_int cmd,u_long arg)
 {
        int dev;
 
-       if ((!inode) || !(inode->i_rdev))
+       if ((!inode) || kdev_none(inode->i_rdev))
                return -EINVAL;
        dev = DEVICE_NR(inode->i_rdev);
 
index e99818753e5e6cd86ce6c814133d926ed565fa2e..543796102d975ab1a4a4a1b5142e826504111afd 100644 (file)
@@ -229,7 +229,7 @@ static int aztcd_blocksizes[1] = { 2048 };
 #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 char aztIndatum;
 static unsigned long aztTimeOutCount;
 static int aztCmd = 0;
 
+static spinlock_t aztSpin = SPIN_LOCK_UNLOCKED;
+
 /*###########################################################################
    Function Prototypes
   ###########################################################################
@@ -1599,10 +1601,6 @@ static void do_aztcd_request(request_queue_t * q)
        }
        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 @@ int __init aztcd_init(void)
                       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 */
index b8b2c17bc45d33dc8f4ccb6d956ae49dcdcb3bcf..20a80e42e854c7a12ab15d74adc71c1ebcfa8c5e 100644 (file)
@@ -1587,7 +1587,7 @@ static void do_cdu31a_request(request_queue_t * q)
        /*
         * 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 @@ static void do_cdu31a_request(request_queue_t * q)
                }
        }
       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 @@ int __init cdu31a_init(void)
                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));
 
index f9b1af285e91ae25146b80bd2302fb359ba9b1bf..411103eadbd528144f384cdfdde86520005b7ee9 100644 (file)
@@ -302,6 +302,7 @@ struct cm206_struct {
 #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 @@ static void do_cm206_request(request_queue_t * q)
                        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 @@ static void do_cm206_request(request_queue_t * q)
                                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 @@ int __init cm206_init(void)
                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 @@ int __init cm206_init(void)
                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);
index c216dfe9688ca1e1d75bf3f649a8e0864a5e0fb7..616ce21f264467de7b96a44733cbcbb719409045 100644 (file)
@@ -162,6 +162,7 @@ static int AudioEnd_m;
 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 @@ static int check_gscd_med_chg(kdev_t full_dev)
        int target;
 
 
-       target = MINOR(full_dev);
+       target = minor(full_dev);
 
        if (target > 0) {
                printk
@@ -283,7 +284,7 @@ static void __do_gscd_request(unsigned long dummy)
        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 @@ static void __do_gscd_request(unsigned long dummy)
                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 @@ int __init my_gscd_init(void)
        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 @@ int __init my_gscd_init(void)
        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;
index 8fd86a65334427c20493abb498ef840643d3e27f..e8145bf932dec2bac84d570a5e7dbf8410808fd8 100644 (file)
@@ -123,7 +123,7 @@ static int mcdPresent;
 #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 int mcd_open(struct cdrom_device_info *cdi, int purpose);
 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 @@ static void do_mcd_request(request_queue_t * q)
 
                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 @@ int __init mcd_init(void)
        }
 
        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 @@ int __init mcd_init(void)
        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");
index 9963409fa876e73eb21dfffd80fbbe2f6fcdf446..8fa24e0aba952f8e7c31f51aad63bebea44756fa 100644 (file)
@@ -291,6 +291,7 @@ static struct s_drive_stuff *mcdx_stuffp[MCDX_NDRIVES];
 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 struct cdrom_device_info mcdx_info = {
 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 @@ void do_mcdx_request(request_queue_t * q)
 
        INIT_REQUEST;
 
-       dev = MINOR(CURRENT->rq_dev);
+       dev = minor(CURRENT->rq_dev);
        stuffp = mcdx_stuffp[dev];
 
        if ((dev < 0)
@@ -598,14 +599,13 @@ void do_mcdx_request(request_queue_t * q)
        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 @@ void do_mcdx_request(request_queue_t * q)
 
                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 @@ static int mcdx_open(struct cdrom_device_info *cdi, int purpose)
 {
        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 @@ static void mcdx_close(struct cdrom_device_info *cdi)
 
        xtrace(OPENCLOSE, "close()\n");
 
-       stuffp = mcdx_stuffp[MINOR(cdi->dev)];
+       stuffp = mcdx_stuffp[minor(cdi->dev)];
 
        --stuffp->users;
 }
@@ -805,7 +800,7 @@ static int mcdx_media_changed(struct cdrom_device_info *cdi, int disc_nr)
        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 @@ int __init mcdx_init_drive(int drive)
                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 @@ int __init mcdx_init_drive(int drive)
                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 @@ mcdx_playtrk(struct s_drive_stuff *stuffp, const struct cdrom_ti *ti)
 
 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_reset(struct s_drive_stuff *stuffp, enum resetmodes mode, int tr
 
 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))
index d8ee1c68c486204263780ce0dfd6f805346da0f3..176c6ce4e9d9230f3f245a578f0402583a3f5215 100644 (file)
@@ -109,7 +109,6 @@ static void debug(int debug_this, const char* fmt, ...)
 #endif
 
 static int blksize = 2048;
-static int hsecsize = 2048;
 
 \f
 /* Drive hardware/firmware characteristics
@@ -267,6 +266,7 @@ static int sleep_timeout;   /* max # of ticks to sleep */
 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 @@ static int update_toc(void)
 
 
 #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 @@ static void do_optcd_request(request_queue_t * q)
 
        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 @@ int __init optcd_init(void)
        }
        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;
index 4b350c2dfb1763b9a799aaba879755965fcac659..e1b7b0cdc46a5826838421f7b8216684c08cdf5e 100644 (file)
@@ -1664,7 +1664,6 @@ static struct block_device_operations sjcd_fops = {
 };
 
 static int blksize = 2048;
-static int secsize = 2048;
 
 /*
  * Following stuff is intended for initialization of the cdrom. It
@@ -1692,7 +1691,6 @@ int __init sjcd_init(void)
        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) {
index aa8d818fb13c45ed69b735f81a5c52bb94c2313b..9a9b3c565b4b250dfe9ba42c464383014f38051e 100644 (file)
@@ -5827,7 +5827,7 @@ static void serial_console_write(struct console *co, const char *s,
 
 static kdev_t serial_console_device(struct console *c)
 {
-       return MKDEV(TTY_MAJOR, 64 + c->index);
+       return mk_kdev(TTY_MAJOR, 64 + c->index);
 }
 
 /*
index 7ba38651f004e504bd8aae4a8f1767f0d786b3c8..8bcc0a2d02a5911630a60e5e27d7246a9226df0d 100644 (file)
@@ -133,8 +133,8 @@ DEB( static int debugging = DEBUG; )
 #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 @@ static int st_open(struct inode *inode, struct file *filp)
        }
        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 @@ static int st_attach(Scsi_Device * SDp)
                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 */
index 86d1023c0c205283e473bd7d50b96a0a8d81668f..25cf21c102ee34871f54a159f8062a428f847bc0 100644 (file)
@@ -1816,7 +1816,7 @@ static int ymf_open(struct inode *inode, struct file *file)
        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_release(struct inode *inode, struct file *file)
 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;
 
index 9cd3f2166bd8d73d1712c790631189b052d4e0fb..0c704dc0710e65504f418f5249297c34708bd79c 100644 (file)
@@ -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
 
  * 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 @@ static void catc_irq_done(struct urb *urb)
                } 
        }
 
-       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");
+       }
 }
 
 /*
@@ -563,6 +570,54 @@ static void catc_set_multicast_list(struct net_device *netdev)
        catc_write_mem_async(catc, 0xfa80, catc->multicast, 64);
 }
 
+/*
+ * 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 @@ static void *catc_probe(struct usb_device *usbdev, unsigned int ifnum, const str
        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;
index ed919d1795b084813d6a3063ac668c217fe3d84e..424fee8dee90531fd0b11daf92c1b7cdf91d09d0 100644 (file)
@@ -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 @@ static int proc_ioctl (struct dev_state *ps, void *arg)
                        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 */
index a1e20fda80944a9f064aa6807a5980c2a8c7e106..b9a5be1e7542a21587689c8f511459357ed336e0 100644 (file)
@@ -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.
  * 
  * 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>
 #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 @@ static int video_nr = -1;
  * 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 @@ static int buf_timeout = 5;
 /* 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;
-
-/* Dump raw pixel data, in one of 3 formats. See ov511_dumppix() for details. */
-static int dumppix = 0;
-
-MODULE_PARM(autoadjust, "i");
-MODULE_PARM_DESC(autoadjust, "CCD dynamically changes exposure");
+static int sensor_gbr; /* = 0 */
+
+/* Dump raw pixel data. */
+static int dumppix; /* = 0 */
+
+/* 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(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(testpat,
+  "Replace image with vertical bar testpattern (only partially working)");
 
-MODULE_AUTHOR( DRIVER_AUTHOR );
-MODULE_DESCRIPTION( DRIVER_DESC );
+// 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");
+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_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 struct cam_list clist[] = {
 };
 
 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 @@ static struct palette_list plist[] = {
 };
 #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 @@ static struct palette_list plist[] = {
 /* 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 @@ static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr)
                        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 @@ static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr)
  * 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 @@ static inline unsigned long kvirt_to_pa(unsigned long adr)
        return ret;
 }
 
-static void *rvmalloc(unsigned long size)
+static void *
+rvmalloc(unsigned long size)
 {
        void *mem;
        unsigned long adr, page;
@@ -294,7 +458,8 @@ static void *rvmalloc(unsigned long size)
        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 @@ extern struct proc_dir_entry *video_proc_entry;
 
 #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 @@ static int ov511_read_proc(char *page, char **start, off_t off,
        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 @@ static void proc_ov511_destroy(void)
 
 /**********************************************************************
  *
- * 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 @@ static int ov511_reg_write(struct usb_device *dev,
 }
 
 /* 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_reg_read(struct usb_device *dev, unsigned char reg)
        }
 }
 
-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 @@ error:
        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 @@ static int ov511_i2c_read(struct usb_device *dev, unsigned char reg)
 
        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 @@ error:
        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 @@ error:
 }
 
 #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,24 +1441,145 @@ static int ov511_reset(struct usb_device *dev, unsigned char reset_type)
 
 /* 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 */
-static inline int ov511_restart(struct usb_device *dev)
+/* 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)
 {
-       PDEBUG(4, "restarting");
-       return (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x00));
-}
+       if (ov511->stopped) {
+               PDEBUG(4, "restarting");
+               ov511->stopped = 0;     
 
-static int ov511_set_packet_size(struct usb_ov511 *ov511, int size)
-{
-       int alt, mult;
+               /* Reinitialize the stream */
+               if (ov511->bridge == BRG_OV518 ||
+                   ov511->bridge == BRG_OV518PLUS)
+                       ov511_reg_write(ov511->dev, 0x2f, 0x80);
 
-       if (ov511_stop(ov511->dev) < 0)
+               return (ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET,
+                                       0x00));
+       }
+
+       return 0;
+}
+
+/* Resets the hardware snapshot button */
+static void 
+ov51x_clear_snapshot(struct usb_ov511 *ov511)
+{
+       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;
+}
+
+/* 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)
+{
+       struct usb_device *dev = ov511->dev;
+
+       if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, write_id) < 0)
+               return -EIO;
+
+       if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, read_id) < 0)
+               return -EIO;
+
+       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;
@@ -780,6 +1607,20 @@ static int ov511_set_packet_size(struct usb_ov511 *ov511, int size)
                        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 @@ static int ov511_set_packet_size(struct usb_ov511 *ov511, int size)
 
        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;
 }
 
-
-static inline int
-ov7610_set_picture(struct usb_ov511 *ov511, struct video_picture *p)
+/* Upload compression params and quantization tables. Returns 0 for success. */
+static int
+ov511_init_compression(struct usb_ov511 *ov511)
 {
-       int ret;
        struct usb_device *dev = ov511->dev;
+       int rc = 0;
+
+       if (!ov511->compress_inited) {
+
+               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;
+               }
+       }
 
-       PDEBUG(4, "ov511_set_picture");
-
-       if (ov511_stop(dev) < 0)
-               return -EIO;
-
-       ov511->contrast = p->contrast;
-       ov511->brightness = p->brightness;
-       ov511->colour = p->colour;
-       ov511->hue = p->hue;
-       ov511->whiteness = p->whiteness;
-
-       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;
+       ov511->compress_inited = 1;
+out:   
+       return rc;
+}
 
-       if (ov511->sensor == SEN_OV7610 || ov511->sensor == SEN_OV6620) {
-               if (ov511_i2c_write(dev, OV7610_REG_CNT, p->contrast >> 8) < 0)
-                       return -EIO;
+/* Upload compression params and quantization tables. Returns 0 for success. */
+static int
+ov518_init_compression(struct usb_ov511 *ov511)
+{
+       struct usb_device *dev = ov511->dev;
+       int rc = 0;
 
-               if (ov511_i2c_write(dev, OV7610_REG_RED, 0xFF - (p->hue >> 8)) < 0)
-                       return -EIO;
+       if (!ov511->compress_inited) {
 
-               if (ov511_i2c_write(dev, OV7610_REG_BLUE, p->hue >> 8) < 0)
-                       return -EIO;
+               if (ov518_upload_quan_tables(dev) < 0) {
+                       err("Error uploading quantization tables");
+                       rc = -EIO;
+                       goto out;
+               }
+       }
 
-               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;
+       ov511->compress_inited = 1;
+out:   
+       return rc;
+}
 
-               cur_sat = ov511_i2c_read(dev, OV7610_REG_BLUE);
+/* -------------------------------------------------------------------------- */
 
-               tmp = (p->hue >> 8) - cur_sat;
-               new_sat = (tmp < 0) ? (-tmp) | 0x80 : tmp;
+/* Sets sensor's contrast setting to "val" */
+static int
+sensor_set_contrast(struct usb_ov511 *ov511, unsigned short val)
+{
+       int rc;
 
-               PDEBUG(1, "cur=%d target=%d diff=%d", cur_sat, p->hue >> 8, tmp); 
+       PDEBUG(3, "%d", val);
 
-               if (ov511_i2c_write(dev, OV7610_REG_BLUE, new_sat) < 0)
+       if (ov511->stop_during_set)
+               if (ov511_stop(ov511) < 0)
                        return -EIO;
 
-               // DEBUG_CODE
-               PDEBUG(1, "hue=%d", ov511_i2c_read(dev, OV7610_REG_BLUE)); 
-
-#endif
+       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;
+       }
        }
 
-       if (ov511_restart(dev) < 0)
+       rc = 0;         /* Success */
+       ov511->contrast = val;
+out:
+       if (ov511_restart(ov511) < 0)
                return -EIO;
 
-       return 0;
+       return rc;
 }
 
-static inline int
-ov7610_get_picture(struct usb_ov511 *ov511, struct video_picture *p)
+/* Gets sensor's contrast setting */
+static int
+sensor_get_contrast(struct usb_ov511 *ov511, unsigned short *val)
 {
-       int ret;
-       struct usb_device *dev = ov511->dev;
+       int rc;
 
-       PDEBUG(4, "ov511_get_picture");
+       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;
+       }
 
-       if (ov511_stop(dev) < 0)
-               return -EIO;
+       PDEBUG(3, "%d", *val);
+       ov511->contrast = *val;
 
-       if ((ret = ov511_i2c_read(dev, OV7610_REG_SAT)) < 0) return -EIO;
-       p->colour = ret << 8;
+       return 0;
+}
 
-       if ((ret = ov511_i2c_read(dev, OV7610_REG_CNT)) < 0) return -EIO;
-       p->contrast = ret << 8;
+/* -------------------------------------------------------------------------- */
 
-       if ((ret = ov511_i2c_read(dev, OV7610_REG_BRT)) < 0) return -EIO;
-       p->brightness = ret << 8;
+/* Sets sensor's brightness setting to "val" */
+static int
+sensor_set_brightness(struct usb_ov511 *ov511, unsigned short val)
+{
+       int rc;
 
-       /* 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;
+       PDEBUG(4, "%d", val);
 
-       p->whiteness = 105 << 8;
+       if (ov511->stop_during_set)
+               if (ov511_stop(ov511) < 0)
+                       return -EIO;
 
-       /* Can we get these from frame[0]? -claudio? */
-       p->depth = ov511->frame[0].depth;
-       p->palette = ov511->frame[0].format;
+       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 0;
+       return rc;
 }
 
-/* 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)
+/* Gets sensor's brightness setting */
+static int
+sensor_get_brightness(struct usb_ov511 *ov511, unsigned short *val)
 {
-       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;
+
+       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;
 }
 
-/* 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 },
-       { 0, 0 }
-};
+/* -------------------------------------------------------------------------- */
 
+/* Sets sensor's saturation (color intensity) setting to "val" */
 static int
-ov511_mode_init_regs(struct usb_ov511 *ov511,
-                    int width, int height, int mode, int sub_flag)
+sensor_set_saturation(struct usb_ov511 *ov511, unsigned short val)
 {
-       int i;
-       struct usb_device *dev = ov511->dev;
-       int hwsbase, hwebase, vwsbase, vwebase, hwsize, vwsize; 
-       int hwscale = 0, vwscale = 0;
+       int rc;
 
-       PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d",
-              width, height, mode, sub_flag);
+       PDEBUG(3, "%d", val);
 
-       if (ov511_stop(ov511->dev) < 0)
-               return -EIO;
+       if (ov511->stop_during_set)
+               if (ov511_stop(ov511) < 0)
+                       return -EIO;
 
-       /* Dumppix only works with RGB24 */
-       if (dumppix && (mode != VIDEO_PALETTE_RGB24)) {
-               err("dumppix only supported with RGB 24");
-               return -EINVAL;
+       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 (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);
+       rc = 0;         /* Success */
+       ov511->colour = val;
+out:
+       if (ov511_restart(ov511) < 0)
+               return -EIO;
 
-               /* 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);
+       return rc;
+}
 
-               /* For snapshot */
-               ov511_reg_write(dev, 0x1e, 0x01);
-               ov511_reg_write(dev, 0x1f, 0x03);
-       }
+/* Gets sensor's saturation (color intensity) setting */
+static int
+sensor_get_saturation(struct usb_ov511 *ov511, unsigned short *val)
+{
+       int rc;
 
-       /* The different sensor ICs handle setting up of window differently */
        switch (ov511->sensor) {
        case SEN_OV7610:
        case SEN_OV7620AE:
-               hwsbase = 0x38;
-               hwebase = 0x3a;
-               vwsbase = vwebase = 0x05;
-               break;
        case SEN_OV6620:
-               hwsbase = 0x38;
-               hwebase = 0x3a;
-               vwsbase = 0x05;
-               vwebase = 0x06;
+       case SEN_OV6630:
+               rc = ov51x_i2c_read(ov511, OV7610_REG_SAT);
+               if (rc < 0)
+                       return rc;
+               else
+                       *val = rc << 8;
                break;
        case SEN_OV7620:
-               hwsbase = 0x2c;
-               hwebase = 0x2d;
-               vwsbase = vwebase = 0x05;
+//             /* 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:
-               err("Invalid sensor");
-               return -EINVAL;
+               PDEBUG(3, "Unsupported with this sensor");
+               return -EPERM;
        }
 
-       /* 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;
+       PDEBUG(3, "%d", *val);
+       ov511->colour = *val;
+
+       return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/* Sets sensor's hue (red/blue balance) setting to "val" */
+static int
+sensor_set_hue(struct usb_ov511 *ov511, unsigned short val)
+{
+       int rc;
+
+       PDEBUG(3, "%d", val);
+
+       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;
+       }
+
+       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;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static inline int
+sensor_set_picture(struct usb_ov511 *ov511, struct video_picture *p)
+{
+       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 */
-                       ov511_i2c_write(dev, 0x14, 0x24);
+                       ret = mode_init_ov_sensor_regs(ov511, width, height,
+                               mode, sub_flag, 1);
+                       if (ret < 0)
+                               return ret;
                        hwsize = 176;
                        vwsize = 144;
                }
-       }
-       else {
+       } else {
                if (width > 320 && height > 240) {  /* VGA */
-                       ov511_i2c_write(dev, 0x14, 0x04);
+                       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 */
-                       ov511_i2c_write(dev, 0x14, 0x24);
+                       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;
+}
+
+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
+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;
+       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) < 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;
+
+       if (mlist518[i].width == 0) {
+               err("Unknown mode (%d, %d): %d", width, height, mode);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/* 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;
+
+       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);
+       }
+
+       if (FATAL_ERROR(rc))
+               return rc;
+
+       switch (ov511->sensor) {
+       case SEN_OV7610:
+       case SEN_OV7620:
+       case SEN_OV7620AE:
+       case SEN_OV8600:
+       case SEN_OV6620:
+       case SEN_OV6630:
+               rc = set_ov_sensor_window(ov511, width, height, mode, sub_flag);
+               break;
+       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("Unknown sensor");
+               rc = -EINVAL;
+       }
+
+       if (FATAL_ERROR(rc))
+               return rc;
+
+       /* Sensor-independent settings */
+       rc = sensor_set_auto_brightness(ov511, ov511->auto_brt);
+       if (FATAL_ERROR(rc))
+               return rc;
+
+       rc = sensor_set_auto_exposure(ov511, ov511->auto_exp);
+       if (FATAL_ERROR(rc))
+               return rc;
+
+       rc = sensor_set_banding_filter(ov511, bandingfilter);
+       if (FATAL_ERROR(rc))
+               return rc;
+
+       if (ov511->lightfreq) {
+               rc = sensor_set_light_freq(ov511, lightfreq);
+               if (FATAL_ERROR(rc))
+                       return rc;
+       }
+
+       rc = sensor_set_backlight(ov511, ov511->backlight);
+       if (FATAL_ERROR(rc))
+               return rc;
+
+       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);
+
+       /* 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->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
+ *
+ **********************************************************************/
+
+/* 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;
        }
-
-       /* 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));
+       default:
+               return -EINVAL;
        }
 
-       for (i = 0; mlist[i].width; i++) {
-               int lncnt, pxcnt, clock;
+       return 0;
+}
 
-               if (width != mlist[i].width || height != mlist[i].height)
-                       continue;
+/* 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 (!mlist[i].color && mode != VIDEO_PALETTE_GREY)
-                       continue;
+               break;
+       }
+       default:
+               sprintf(name, "%s", "Camera");
+       }
 
-               /* 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);
-               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;
+}
 
-               /* 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));
-               else
-                       ov511_i2c_write(dev, 0x12, mlist[i].common_A | (testpat?0x02:0x00));
+/* Set norm (NTSC, PAL, SECAM, AUTO) */
+static int 
+decoder_set_norm(struct usb_ov511 *ov511, int norm)
+{
+       PDEBUG(4, "%d", norm);
 
-               /* 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);
+       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;
        }
-
-       if (compress) {
-               ov511_reg_write(dev, 0x78, 0x03); // Turn on Y compression
-               ov511_reg_write(dev, 0x79, 0x00); // Disable LUTs
-       }
-
-       if (ov511_restart(ov511->dev) < 0)
-               return -EIO;
-
-       if (mlist[i].width == 0) {
-               err("Unknown mode (%d, %d): %d", width, height, mode);
+       default:
                return -EINVAL;
        }
 
-#ifdef OV511_DEBUG
-       if (debug >= 5)
-               ov511_dump_i2c_regs(dev);
-#endif
-
        return 0;
 }
 
+
 /**********************************************************************
  *
  * Color correction functions
@@ -1169,7 +3237,7 @@ ov511_mode_init_regs(struct usb_ov511 *ov511,
 
 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 @@ ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v,
 
        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[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[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 @@ ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v,
  *     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 @@ ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v,
  * 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;
-               }
-               pOut += iWidth*3*2;
-       }
-
-       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;
+       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;
        }
 
+       /* Copy Y */
        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;
+       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;
                        }
-                       pOut1 += (iWidth - 8) * 3;
                }
-               pOut += 8 * 3;
+               pOutLine += 8 * frame->rawwidth;
        }
 }
 
-static void
-ov511_parse_yuv420_to_rgb(unsigned char *pIn0, unsigned char *pOut0,
-                      int iOutY, int iOutUV, int iHalf, int iWidth, int bits)
-{
-       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;
-               }
+/*
+ * 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 */
        }
 
-       /* 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;
-       }
-
-       /* 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;
-               }
+       /* 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 */
        }
 }
 
-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;
-
-       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;
+/**********************************************************************
+ *
+ * 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");
                }
-               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;
+       } 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");
                }
-               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;
+       } else {
+               err("Unknown bridge");
+       }
+
+       if (ov511->decomp_ops) {
+               if (!ov511->decomp_ops->decomp_lock) {
+                       ov511->decomp_ops = NULL;
+                       unlock_kernel();
+                       return -ENOSYS;
                }
-               break;
-       } /* End switch (dumppix) */
+               ov511->decomp_ops->decomp_lock();
+               unlock_kernel();
+               return 0;
+       } else {
+               unlock_kernel();
+               return -ENXIO;
+       }
 }
 
-/* This converts YUV420 segments to YUYV */
-static void
-ov511_parse_data_yuv422(unsigned char *pIn0, unsigned char *pOut0,
-                       int iOutY, int iOutUV, int iWidth)
+/* 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;
-       unsigned char *pIn, *pOut, *pOut1;
+       int released = 0;       /* Did we actually do anything? */
+
+       if (!ov511)
+               return;
+
+       lock_kernel();
+
+       if (ov511->decomp_ops && ov511->decomp_ops->decomp_unlock) {
+               ov511->decomp_ops->decomp_unlock();
+               released = 1;
+       }
+
+       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");
+       }
+}
+
+/**********************************************************************
+ *
+ * Format conversion
+ *
+ **********************************************************************/
+
+/* 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;
+
+                       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++;
-               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++;
+       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 + 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;
                        }
-                       pOut1 += iWidth - 8;
+                       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;
+                       }
+                       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);
+               }
+
+               return;
+       }
+
+       /* 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);
+       }
+
+       /* 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
 
-       /* Don't bother with little images */
-       if (frame->width < 400) 
-               return;
+       /* 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));
 
-       /* 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 */
+               yuv420p_to_yuv422p(frame, frame->data);
+               break;
+       default:
+               err("Cannot convert data to this format");
        }
 
-       /* 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 */
-       }
+       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 @@ static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
                        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 @@ static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
 
                                        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 @@ static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
                        /* 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 @@ static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
                        }
 
                        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;
+               }
 
-               /* 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);
-               } else {
-                       pData = &cdata[iPix = 9];
-               }
-
-               /* 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);
+#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;
                        }
 
-                       /* 
-                        * 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 (skip) {
+                               PDEBUG(5, "Skipping packet (all zero)");
+                               continue;
                        }
-
-                       pData = &cdata[iPix];
                }
+#endif
+               /* If frame start, skip header */
+               if (frame->bytes_recvd == 0)
+                       offset = 9;
+               else
+                       offset = 0;
 
-               /* 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);
+               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
-                               ov511->scratchlen = 0;
+                               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]);
+               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]);
+               }
+
+               /* 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 {
+                       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));
+
+               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);
+                       }
+
+                       /* 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;
+                                       }
+                               }
+
+                               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;
+               }
+       }
 
        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 @@ static void ov511_isoc_irq(struct urb *urb)
                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 {
                        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_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 {
+       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 @@ static int ov511_init_isoc(struct usb_ov511 *ov511)
                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 @@ static int ov511_init_isoc(struct usb_ov511 *ov511)
                        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 void ov511_stop_isoc(struct usb_ov511 *ov511)
        }
 }
 
-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 @@ static int ov511_new_frame(struct usb_ov511 *ov511, int framenum)
        /* 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 @@ static int ov511_new_frame(struct usb_ov511 *ov511, int framenum)
  * 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 @@ static int ov511_alloc(struct usb_ov511 *ov511)
        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 @@ static int ov511_alloc(struct usb_ov511 *ov511)
                                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 @@ error:
  * - 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 @@ static void ov511_buf_callback(unsigned long data)
        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 void ov511_dealloc(struct usb_ov511 *ov511, int now)
  *
  ***************************************************************************/
 
-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 @@ static int ov511_open(struct video_device *dev, int flags)
 
        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 @@ static int ov511_open(struct video_device *dev, int flags)
        }
 
        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 @@ static void ov511_close(struct video_device *dev)
        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 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
        {
                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 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
        {
                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 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
        }
        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 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
        {
                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 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
                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 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
        {
                int vf;
 
-               PDEBUG (4, "VIDIOCGCAPTURE");
+               PDEBUG(4, "VIDIOCGCAPTURE");
 
                if (copy_from_user(&vf, arg, sizeof(vf)))
                        return -EFAULT;
@@ -2357,6 +4997,8 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
        {
                struct video_capture vc;
 
+               PDEBUG(4, "VIDIOCSCAPTURE");
+
                if (copy_from_user(&vc, arg, sizeof(vc)))
                        return -EFAULT;
                if (vc.flags)
@@ -2391,7 +5033,7 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
                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 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
                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 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
                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 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
        {
                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 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
                        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)
-                       return -EBUSY;
-
-               /* Don't compress if the size changed */
+               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;
+               }
+
                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 @@ redo:
                        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 (ov511->frame[frame].grabstate == FRAME_ERROR) {
+                       if (rc)
+                               return rc;
+
+                       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 @@ redo:
        {
                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 @@ redo:
 
                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 @@ static long ov511_read(struct video_device *dev, char *buf, unsigned long count,
                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,138 +5445,396 @@ static long ov511_read(struct video_device *dev, char *buf, unsigned long count,
        }
 
        /* 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");
+       if (ov511->snap_enabled && !frame->snapshot) {
+               frame->bytes_read = 0;
+               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;
+               ov51x_clear_snapshot(ov511);
+       }
+
+       /* 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) 
+//         > get_frame_length((struct ov511_frame *)frame))
+//             count = frame->scanlength - frame->bytes_read;
+
+       /* FIXME - count hardwired to be one frame... */
+       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);
+               rc = -EFAULT;
+               goto error;
+       }
+
+       frame->bytes_read += count;
+       PDEBUG(4, "{copy} count used=%ld, new bytes_read=%ld",
+               count, frame->bytes_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 ((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 *vdev, const char *adr, unsigned long size)
+{
+       struct usb_ov511 *ov511 = vdev->priv;
+       unsigned long start = (unsigned long)adr;
+       unsigned long page, pos;
+
+       if (ov511->dev == NULL)
+               return -EIO;
+
+       PDEBUG(4, "mmap: %ld (%lX) bytes", size, size);
+
+       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)) {
+                       up(&ov511->lock);
+                       return -EAGAIN;
+               }
+               start += PAGE_SIZE;
+               pos += PAGE_SIZE;
+               if (size > PAGE_SIZE)
+                       size -= PAGE_SIZE;
+               else
+                       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,
+       open:           ov511_open,
+       close:          ov511_close,
+       read:           ov511_read,
+       write:          ov511_write,
+       ioctl:          ov511_ioctl,
+       mmap:           ov511_mmap,
+       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;
 
-       /* Repeat until we get a snapshot frame */
-       if (ov511->snap_enabled)
-               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");
-               goto restart;
-       }
+               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;
+               }
 
-       /* 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);
+               return 0;
        }
+       case OV511IOC_GUINT:
+       {
+               struct ov511_uint_opt opt;
 
-       PDEBUG(4, "frmx=%d, bytes_read=%ld, scanlength=%ld", frmx,
-               frame->bytes_read, frame->scanlength);
+               if (copy_from_user(&opt, arg, sizeof(opt)))
+                       return -EFAULT;
 
-       /* copy bytes to user space; we allow for partials reads */
-//     if ((count + frame->bytes_read) > frame->scanlength)
-//             count = frame->scanlength - frame->bytes_read;
+               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;
+               }
 
-       /* FIXME - count hardwired to be one frame... */
-       count = frame->width * frame->height * (frame->depth >> 3);
+               if (copy_to_user(arg, &opt, sizeof(opt)))
+                       return -EFAULT;
 
-       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;
+               return 0;
        }
+       case OV511IOC_SUINT:
+       {
+               struct ov511_uint_opt opt;
 
-       frame->bytes_read += count;
-       PDEBUG(4, "{copy} count used=%ld, new bytes_read=%ld",
-               count, frame->bytes_read);
+               if (copy_from_user(&opt, arg, sizeof(opt)))
+                       return -EFAULT;
 
-       if (frame->bytes_read >= frame->scanlength) { /* All data has been read */
-               frame->bytes_read = 0;
+               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;
+               }
 
-               /* Mark it as available to be used again. */
-               ov511->frame[frmx].grabstate = FRAME_UNUSED;
-               if (ov511_new_frame(ov511, !frmx))
-                       err("ov511_new_frame returned error");
+               return 0;
        }
+       case OV511IOC_WI2C:
+       {
+               struct ov511_i2c_struct w;
 
-       PDEBUG(4, "read finished, returning %ld (sweet)", count);
+               if (copy_from_user(&w, arg, sizeof(w)))
+                       return -EFAULT;
 
-       return count;
-}
+               return ov51x_i2c_write_slave(ov511, w.slave, w.reg, w.value,
+                       w.mask);
+       }
+       case OV511IOC_RI2C:
+       {
+               struct ov511_i2c_struct r;
 
-static int ov511_mmap(struct video_device *dev, const char *adr,
-       unsigned long size)
-{
-       struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
-       unsigned long start = (unsigned long)adr;
-       unsigned long page, pos;
+               if (copy_from_user(&r, arg, sizeof(r)))
+                       return -EFAULT;
 
-       if (ov511->dev == NULL)
-               return -EIO;
+               rc = ov51x_i2c_read_slave(ov511, r.slave, r.reg);
+               if (rc < 0)
+                       return rc;
 
-       PDEBUG(4, "mmap: %ld (%lX) bytes", size, size);
+               r.value = rc;
 
-       if (size > (((OV511_NUMFRAMES * MAX_DATA_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))
-               return -EINVAL;
+               if (copy_to_user(arg, &r, sizeof(r)))
+                       return -EFAULT;
 
-       pos = (unsigned long)ov511->fbuf;
-       while (size > 0) {
-               page = kvirt_to_pa(pos);
-               if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED))
-                       return -EAGAIN;
-               start += PAGE_SIZE;
-               pos += PAGE_SIZE;
-               if (size > PAGE_SIZE)
-                       size -= PAGE_SIZE;
-               else
-                       size = 0;
+               return 0;
        }
+       default:
+               return -EINVAL;
+       } /* end switch */
 
        return 0;
 }
-
-static struct video_device ov511_template = {
-       name:           "OV511 USB Camera",
-       type:           VID_TYPE_CAPTURE,
-       hardware:       VID_HARDWARE_OV511,
-       open:           ov511_open,
-       close:          ov511_close,
-       read:           ov511_read,
-       write:          ov511_write,
-       ioctl:          ov511_ioctl,
-       mmap:           ov511_mmap,
-       initialize:     ov511_init_done,
-};
+#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 @@ static int ov76xx_configure(struct usb_ov511 *ov511)
                { 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 int ov76xx_configure(struct usb_ov511 *ov511)
        };
 
        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)
+       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;
 
-       if (ov511_reset(dev, OV511_RESET_NOREGS) < 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 @@ static int ov76xx_configure(struct usb_ov511 *ov511)
                        }
                }
 
-               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);
-                       return -1;
+                       ov511->sensor = SEN_OV7620AE;
                }
-       } else {        /* sensor != 0; user overrode detection */
-               ov511->sensor = sensor;
-               info("Sensor set to type %d", ov511->sensor);
+       } 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(dev, aRegvalsNorm7620))
+               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 {
+               err("Error: Sensor is an unsupported KS0122");
+               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;
-
-       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");
+       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;
+
+       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);
 
-       if (ov511_write_regvals(dev, aRegvalsInit)) goto error;
-       if (ov511_write_regvals(dev, aRegvalsNorm511)) goto error;
+       /* 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_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)
+               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_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ,
-                                   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 @@ error:
  ***************************************************************************/
 
 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 @@ ov511_probe(struct usb_device *dev, unsigned int ifnum,
 
        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 PROD_OV518:
+               info("USB OV518 camera found");
+               ov511->bridge = BRG_OV518;
+               ov511->bclass = BCL_OV518;
                break;
-       case 0x0002:
-               if (dev->descriptor.idVendor != 0x0813)
+       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 @@ ov511_disconnect(struct usb_device *dev, void *ptr)
 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 struct usb_driver ov511_driver = {
  *
  ***************************************************************************/
 
-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 @@ static int __init usb_ov511_init(void)
        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);
index a4ef7dacde5b8d47090007bc9a17e72444cfa53c..a3f55f7e5536f021c2435691260ef78672712639 100644 (file)
@@ -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 @@ if (debug >= level) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , ## args
 
 /* 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 @@ if (debug >= level) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , ## args
 #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 @@ if (debug >= level) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , ## args
 #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,45 +210,75 @@ if (debug >= level) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , ## args
                                        /* 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 */
        STATE_HEADER,           /* Parsing header */
@@ -222,7 +292,77 @@ enum {
        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_regvals {
 };
 
 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 @@ struct usb_ov511 {
        /* 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 @@ struct usb_ov511 {
        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 @@ struct palette_list {
        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;
 };
 
-#endif
+/* 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
index 3d8eb253a3abd4fe8b626dcaebf8c4e176f59fc2..a6ba654b621c1412917a92c768a67e387f151373 100644 (file)
@@ -1008,6 +1008,8 @@ int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value)
 
        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 @@ int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value)
 
        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 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
                        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;
        }
 
index 91ed0880f9ebc94f4a0f0c5348b454b3c7ae4124..a7938e8fd9c84e97eea83da1279b519cdc1cd499 100644 (file)
 
 /* 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 @@ static __devinitdata struct usb_device_id pwc_device_table [] = {
        { 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 @@ static void pwc_isoc_cleanup(struct pwc_device *pdev)
        /* 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 @@ static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *ar
                         */
                        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 @@ static void *usb_pwc_probe(struct usb_device *udev, unsigned int ifnum, const st
                        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 @@ static void *usb_pwc_probe(struct usb_device *udev, unsigned int ifnum, const st
        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 @@ static void usb_pwc_disconnect(struct usb_device *udev, void *ptr)
 {
        struct pwc_device *pdev;
        int hint;
+       DECLARE_WAITQUEUE(wait, current);
 
        lock_kernel();
        free_mem_leak();
@@ -1833,13 +1858,19 @@ static void usb_pwc_disconnect(struct usb_device *udev, void *ptr)
                         */
                        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 @@ static int __init usb_pwc_init(void)
        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) {
index 19b267a4cdd11b38e4144e4c7e852b55caeffa94..6807cab91e8c4c90c2d955c1c29fd8a09a7338db 100644 (file)
@@ -79,8 +79,8 @@ struct pwc_whitebalance
 /* 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  */
 };
 
 
index 2b897700fa49ffaabc0c6f543520b46c438c6ae7..28ebbb6c7215c7f45f679ba2d2fcebad6965ca35 100644 (file)
@@ -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 */
index db1f4ff4f818cff6b6b806cc5357be9f4ccbbe1d..d51e1d1efed98101f6786e98740db93ff0aa9362 100644 (file)
@@ -116,6 +116,7 @@ struct usb_serial {
 
 /**
  * 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 @@ struct usb_serial {
  * 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;
index 7b476f2f65f77d8dc33adfc4c0c6771c46f43337..808c82985cab271ed50c6800a64711a818be44cc 100644 (file)
@@ -506,6 +506,7 @@ static int serial_open (struct tty_struct *tty, struct file * filp)
        struct usb_serial *serial;
        struct usb_serial_port *port;
        unsigned int portNumber;
+       int retval;
        
        dbg(__FUNCTION__);
 
@@ -527,10 +528,16 @@ static int serial_open (struct tty_struct *tty, struct file * filp)
         
        /* 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 @@ static void serial_close(struct tty_struct *tty, struct file * filp)
        /* 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 @@ static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum,
        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 @@ static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum,
 
        /* 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 */
index 292101a812e03377ded7bfa64c35a95e6b79c0d3..ce34eb15a8d61c3c8f888c090a247a6d6358f00c 100644 (file)
  *
  * 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.
 /*
  * 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 int  visor_ioctl             (struct usb_serial_port *port, struct file * file, unsi
 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 @@ MODULE_DEVICE_TABLE (usb, id_table);
 
 /* 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 @@ static struct usb_serial_device_type handspring_device = {
 
 /* 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 @@ static struct usb_serial_device_type clie_3_5_device = {
        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 @@ static int visor_open (struct usb_serial_port *port, struct file *filp)
        down (&port->sem);
        
        ++port->open_count;
-       MOD_INC_USE_COUNT;
        
        if (port->open_count == 1) {
                bytes_in = 0;
@@ -321,8 +328,6 @@ static void visor_close (struct usb_serial_port *port, struct file * filp)
 
        /* 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 @@ static int  visor_startup (struct usb_serial *serial)
        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)
 {
index df3721b63871b90133f13efb8213aba128070291..d1aca3be8627b9b359e15568500d8cda4a41c73e 100644 (file)
@@ -208,7 +208,7 @@ static int datafab_read_data(struct us_data *us,
 
                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 @@ static int datafab_write_data(struct us_data *us,
 
                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 @@ int datafab_transport(Scsi_Cmnd * srb, struct us_data *us)
        };
 
        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;
index 28de67b24f670207b8c3c0c510b2d50d07aa6755..465af85d7b5f0e39632be42c9fff0e75fc47f0bc 100644 (file)
@@ -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 @@ freecom_ide_write (struct us_data *us, int reg, int value)
 
         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 @@ int freecom_transport(Scsi_Cmnd *srb, struct us_data *us)
                /* 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 @@ int freecom_transport(Scsi_Cmnd *srb, struct us_data *us)
          * 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) {
index 6b17cd36df561672f0125878dfec3a3cb2836c31..94b9941cff01dad561329c7e7c6d1ca12778908a 100644 (file)
@@ -284,7 +284,7 @@ static int jumpshot_read_data(struct us_data *us,
 
                 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 @@ static int jumpshot_write_data(struct us_data *us,
 
                 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 @@ int jumpshot_transport(Scsi_Cmnd * srb, struct us_data *us)
 
 
        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;
index 7235400dd89c740fe075a8bfa5547868f66d0deb..10227ee44c160153a631e9f717ce1feba7159160 100644 (file)
@@ -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 @@ static int sddr09_send_control(struct us_data *us,
        // 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 @@ int sddr09_read_data(struct us_data *us,
 
        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 @@ int sddr09_read_map(struct us_data *us) {
 
        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 @@ int sddr09_read_map(struct us_data *us) {
                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 @@ int sddr09_transport(Scsi_Cmnd *srb, struct us_data *us)
 
        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));
index 1b8926727a4154e42445b747f3908c6fbaea53bf..602b0f75f7defcce90bb381def39351081825f6a 100644 (file)
@@ -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 @@ int usbat_handle_read10(struct us_data *us,
 
        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]);
index 4b6dd33be9ed3bf8cb06e3192ed150a8b76edf88..f95000fed468144231f18e0b9bc9bb147c29bef5 100644 (file)
@@ -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 @@ int usb_stor_control_msg(struct us_data *us, unsigned int pipe,
        devrequest *dr;
 
        /* allocate the device request structure */
-       dr = kmalloc(sizeof(devrequest), GFP_KERNEL);
+       dr = kmalloc(sizeof(devrequest), GFP_NOIO);
        if (!dr)
                return -ENOMEM;
 
index dcdc144117116c832b4649238592df5a907475fc..729c5ff602e78a8ffcaab6990c644d1d51d0233e 100644 (file)
@@ -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 @@ UNUSUAL_DEV(  0x03f0, 0x0207, 0x0001, 0x0001,
                "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 @@ UNUSUAL_DEV(  0x04cb, 0x0100, 0x0000, 0x2210,
                "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 @@ UNUSUAL_DEV(  0x050d, 0x0115, 0x0133, 0x0133,
                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 @@ UNUSUAL_DEV(  0x054c, 0x0032, 0x0000, 0x9999,
                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 @@ UNUSUAL_DEV(  0x066b, 0x0105, 0x0100, 0x0100,
                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 @@ UNUSUAL_DEV(  0x07ab, 0xfc01, 0x0000, 0x9999,
                 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 @@ UNUSUAL_DEV( 0x07c4, 0xa006, 0x0000, 0xffff,
                "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 @@ UNUSUAL_DEV(  0x0bf6, 0xa001, 0x0100, 0x0110,
                 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),
index 0f7f43f96df879e0c7001b2325f7eaa0a322dd64..624b30e1cb2a87cee98034d15bcf33ba274ee7ac 100644 (file)
@@ -150,9 +150,13 @@ static void usb_drivers_purge(struct usb_driver *driver,struct usb_device *dev)
                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 @@ static int usb_find_interface_driver(struct usb_device *dev, unsigned ifnum)
                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 @@ static int usb_find_interface_driver(struct usb_device *dev, unsigned ifnum)
                        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 @@ void usb_disconnect(struct usb_device **pdev)
                        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);
index 9ca8765c792ef263b9192b0ddb61cca8fe3b7917..70bcf013f00cefbf28f75fbdee31d6ad98d71c14 100644 (file)
@@ -1571,7 +1571,7 @@ static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net)
        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;
index f1611624f695bfda643568b3e614dd31e6f35e38..1b695e5d1db174f2e4b44942ff4c0f56b4b43529 100644 (file)
@@ -39,7 +39,7 @@ void __adfs_error(struct super_block *sb, const char *function, const char *fmt,
        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 super_block *adfs_read_super(struct super_block *sb, void *data, int sile
        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 @@ struct super_block *adfs_read_super(struct super_block *sb, void *data, int sile
        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 @@ struct super_block *adfs_read_super(struct super_block *sb, void *data, int sile
        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 @@ struct super_block *adfs_read_super(struct super_block *sb, void *data, int sile
        } else {
                if (!silent)
                        printk(KERN_ERR "VFS: Unsupported blocksize on dev "
-                               "%s.\n", bdevname(dev));
+                               "%s.\n", sb->s_id);
                goto error;
        }
 
index c5803c5f4792d114dad088b1c96f1b3438e561c7..1ac692e33a1f2cd9be2e1d1244b1cee7b0aa23dd 100644 (file)
@@ -453,7 +453,7 @@ affs_error(struct super_block *sb, const char *function, const char *fmt, ...)
        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 @@ affs_warning(struct super_block *sb, const char *function, const char *fmt, ...)
        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);
 }
 
index c7cfd2258593768ef624152a8d11023eab6226b0..697c8b47df9724b98a0bdb29c1d507a25f5a3a09 100644 (file)
@@ -282,7 +282,7 @@ affs_init_bitmap(struct super_block *sb)
 
        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 @@ affs_init_bitmap(struct super_block *sb)
                }
                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;
                }
index c133fa3c68d77b784ddb9418697bfc90f5b2b97a..b4653c37fa894bbc4b8895e4dea330c3a7422bc5 100644 (file)
@@ -300,7 +300,7 @@ affs_read_super(struct super_block *sb, void *data, int silent)
                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 @@ affs_read_super(struct super_block *sb, void *data, int silent)
        }
        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 @@ got_root:
        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 @@ got_root:
                        break;
                default:
                        printk(KERN_ERR "AFFS: Unknown filesystem on device %s: %08X\n",
-                               kdevname(dev), chksum);
+                               sb->s_id, chksum);
                        goto out_error;
        }
 
index 8dd48f0187cb6d9e1efddf5d88beccd23ca78568..266186cc31aa307293b3b9470c31efd0cd83d2bd 100644 (file)
@@ -33,7 +33,7 @@ static int bfs_readdir(struct file * f, void * dirent, filldir_t filldir)
 
        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 @@ static int bfs_unlink(struct inode * dir, struct dentry * dentry)
                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;
        }
index 8efedbf7710b14ef7ff25f7bf144b47a34ec6396..402b936da0ba398527d2c99e41f598d2f178fb15 100644 (file)
@@ -40,7 +40,7 @@ static void bfs_read_inode(struct inode * inode)
        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 @@ static void bfs_read_inode(struct inode * inode)
        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 @@ static void bfs_write_inode(struct inode * inode, int unused)
        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 @@ static void bfs_write_inode(struct inode * inode, int unused)
        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 @@ static void bfs_delete_inode(struct inode * inode)
        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 @@ static struct super_block * bfs_read_super(struct super_block * s,
        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;
index 826e32126fb9698870ba7e90e1f569b7b5b9a3b9..7300732d467420cc6791e622758e91c0a8c3c5ed 100644 (file)
@@ -293,7 +293,7 @@ static int coda_psdev_open(struct inode * inode, struct file * file)
        int idx;
 
        lock_kernel();
-       idx = MINOR(inode->i_rdev);
+       idx = minor(inode->i_rdev);
        if(idx >= MAX_CODADEVS) {
                unlock_kernel();
                return -ENODEV;
index 9aa28ef93510666e5924e717990d064909c0196d..639ae7464c6ce960115a51baa05d8f67e05cf9fa 100644 (file)
@@ -894,7 +894,7 @@ void devfs_put (devfs_handle_t de)
     {
        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 @@ devfs_handle_t devfs_register (devfs_handle_t dir, const char *name,
     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 @@ devfs_handle_t devfs_register (devfs_handle_t dir, const char *name,
     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 @@ devfs_handle_t devfs_register (devfs_handle_t dir, const char *name,
     {
        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 @@ static int check_disc_changed (struct devfs_entry *de)
 {
     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 @@ static struct inode *_devfs_get_vfs_inode (struct super_block *sb,
     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)
        {
index e09c869dfb2c6fb8d1ab6c3d4fd20d4ef93f3a7e..84f797d28015628862d5e2ee42976459cda789b1 100644 (file)
@@ -267,7 +267,7 @@ kdev_t devfs_alloc_devnum (char type)
        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 @@ kdev_t devfs_alloc_devnum (char type)
     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 @@ void devfs_dealloc_devnum (char type, kdev_t devnum)
     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;
index a9e111930ed9613f340c3fb770dcde824c8f6020..3c3040e7513d830b6be3593ade4e24fd6456af4e 100644 (file)
@@ -1135,9 +1135,8 @@ static int ext2_update_inode(struct inode * inode, int do_sync)
                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;
                }
        }
index 847c9a200ea44a9562b138b27ebecd377aa74a18..05ae46d5cd145ee47aab7911d09739c8416c797b 100644 (file)
@@ -53,9 +53,9 @@ void ext2_error (struct super_block * sb, const char * function,
            (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 @@ NORET_TYPE void ext2_panic (struct super_block * sb, const char * function,
        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 @@ void ext2_warning (struct super_block * sb, const char * function,
        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 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data,
        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 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data,
        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 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data,
        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 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data,
                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 @@ int ext2_remount (struct super_block * sb, int * flags, char * data)
                                               ~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;
                }
                /*
index 59ee8ad176acd1b0aa8940cdf388833c10f684bb..0800f6845d32bd0b3f60aae27c682dcffc5427d2 100644 (file)
@@ -189,10 +189,6 @@ void ext3_free_inode (handle_t *handle, struct inode * inode)
        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));
index 6706e7af5e83d73634e2f22cde7068324c493ab0..d0206215abef371f22054ab6181ac58b29f17d19 100644 (file)
@@ -163,7 +163,7 @@ static void ext3_handle_error(struct super_block *sb)
 
        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 @@ void ext3_error (struct super_block * sb, const char * function,
        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 @@ void __ext3_std_error (struct super_block * sb, const char * function,
        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 @@ void ext3_abort (struct super_block * sb, const char * function,
 
        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 @@ NORET_TYPE void ext3_panic (struct super_block * sb, const char * function,
        /* 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 @@ void ext3_warning (struct super_block * sb, const char * function,
        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 @@ static void dump_orphan_list(struct super_block *sb, struct ext3_sb_info *sbi)
        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 @@ static int ext3_setup_super(struct super_block *sb, struct ext3_super_block *es,
                        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 @@ static void ext3_orphan_cleanup (struct super_block * sb,
 
        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 @@ static void ext3_orphan_cleanup (struct super_block * sb,
 
        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 @@ struct super_block * ext3_read_super (struct super_block * sb, void * data,
        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 @@ struct super_block * ext3_read_super (struct super_block * sb, void * data,
                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 @@ struct super_block * ext3_read_super (struct super_block * sb, void * data,
        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 @@ struct super_block * ext3_read_super (struct super_block * sb, void * data,
            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 @@ struct super_block * ext3_read_super (struct super_block * sb, void * data,
                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 @@ int ext3_remount (struct super_block * sb, int * flags, char * data)
                                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;
                        }
                        /*
index 2f4f389cba3f0d05d8de6ac4bc435988987490ec..540d9df2ba0675d9ef716faaacdc65ec78e71753 100644 (file)
@@ -189,7 +189,7 @@ static void list_cache(void)
 
        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("-- ");
index bec271305515849e088f6a8181c0429651308d9e..e6daf9dc5f17489b5572cec387add764f43ccbdc 100644 (file)
@@ -799,7 +799,7 @@ out_unload_nls:
 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 @@ retry:
        }
        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;
index 6989712116fae45d635e07eb555c8e0da11fbb0b..6f175afac90d9c01f6e21ab78f1dc55bc0ab6082 100644 (file)
@@ -44,7 +44,7 @@ void fat_fs_panic(struct super_block *s,const char *msg)
 
        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");
 }
index 16c7eb194b3016e81ab458694e918a3740d0938c..3811dc5e5099b9ec3b547f22ad0c1b00b2b6b78e 100644 (file)
@@ -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"
 
 
 /*
index fb3eeeb9f32d9ca639823c9205519b6316833b3c..ed31e0a06843c7264f3e0e4cdec2f6a00d37e00c 100644 (file)
@@ -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,49 +64,47 @@ vxfs_typdump(struct vxfs_typed *typ)
  *   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;
 
-       if (!(iblock / (vip->vii_ext4.ve4_indsize * vip->vii_ext4.ve4_indsize * bsize >> 2))) {
-               struct buffer_head      *bp;
-               daddr_t                 pblock;
+               buf = sb_bread(sb, vip->vii_ext4.ve4_indir[0]);
+               if (!buf || !buffer_mapped(buf))
+                       goto fail_buf;
 
-               /*
-                * 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;
 }
 
 /**
index 32f37e612f481db7116f427ee9e6ccc3428473a2..1f76609ee08c3aa049e73e80ec823fc79109d040 100644 (file)
@@ -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 struct file_operations vxfs_dir_operations;
 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);
 
index 942bfa087884561897f35e3a93144125f5ff97da..faf0069126465db441c70674844e9cb1f81a4bb5 100644 (file)
@@ -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 @@ vxfs_getfsh(struct inode *ip, int which)
        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);
index 023bf75a7ff54939c4a055c5ee4f534965e06d2d..98060cfd5189277fb2a310014f817779fd0b388c 100644 (file)
  * 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 address_space_operations vxfs_immed_aops;
 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 @@ vxfs_blkiget(struct super_block *sbp, u_long extent, ino_t ino)
                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 @@ __vxfs_iget(ino_t ino, struct inode *ilistp)
        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 @@ __vxfs_iget(ino_t ino, struct inode *ilistp)
                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 --git a/fs/freevxfs/vxfs_kcompat.h b/fs/freevxfs/vxfs_kcompat.h
new file mode 100644 (file)
index 0000000..342a4cc
--- /dev/null
@@ -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 */
index bc64fd8b527d7a9dd8dcd5fd09577fb29f700ff0..2942766f27f456049b41f4a2df29081cb6106677 100644 (file)
@@ -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 @@ vxfs_find_entry(struct inode *ip, struct dentry *dp, struct page **ppp)
                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 @@ vxfs_readdir(struct file *fp, void *retp, filldir_t filler)
                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 @@ vxfs_readdir(struct file *fp, void *retp, filldir_t filler)
 done:
        fp->f_pos = ((page << PAGE_CACHE_SHIFT) | offset) + 2;
 out:
-       fp->f_version = ip->i_version;
        return 0;
 }
index 44bfbd06671cb321c376a1059fb0b54cef5d4094..6b45a5a5c03bd895f75752ff7686487717c1c312 100644 (file)
@@ -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,8 +85,7 @@ vxfs_read_olt(struct super_block *sbp, u_long bsize)
        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;
 
@@ -96,6 +95,16 @@ vxfs_read_olt(struct super_block *sbp, u_long bsize)
                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;
+       }
+
        oaddr = (char *)bp->b_data + op->olt_size;
        eaddr = (char *)bp->b_data + (infp->vsi_oltsize * sbp->s_blocksize);
 
index 87531b097c4ee6c45e519222e6d25262a5037889..0b1a480e50a56e25019a478a467810d4cbdc8966 100644 (file)
@@ -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 @@ struct address_space_operations vxfs_aops = {
  *   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,
index 99b265f0acf4ff0e2dd7efc28d06db8d516ac689..768e91bb371192b263bf97c7cfa966b5701c8468 100644 (file)
@@ -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 @@ static struct super_operations vxfs_super_ops = {
        .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 @@ vxfs_read_super(struct super_block *sbp, void *dp, int silent)
 {
        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 @@ vxfs_read_super(struct super_block *sbp, void *dp, int silent)
 #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;
        }
 
index 034e9fca67fda06e008edd47ce801964a3c5b392..00e882a152f16dbfe6cf76f3ee3fea49a2f17c6e 100644 (file)
@@ -311,14 +311,11 @@ struct inode *hfs_iget(struct hfs_cat_entry *entry, ino_t type,
                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;
index d3343525acf520c0e5df4bb78ff7f2b83ed60ef5..90244a91602d191e7342dc8991d1a596e38eb633 100644 (file)
@@ -425,7 +425,7 @@ struct super_block *hfs_read_super(struct super_block *s, void *data,
        if (!mdb) {
                if (!silent) {
                        hfs_warn("VFS: Can't find a HFS filesystem on dev %s.\n",
-                              kdevname(dev));
+                              s->s_id);
                }
                goto bail2;
        }
index 678067a83c7f2c683dc9e1600ec9e56e4f5f6ef1..6d8f486be2b9d5158d6a3a649f96de3122486adb 100644 (file)
@@ -362,7 +362,6 @@ int hpfs_remount_fs(struct super_block *s, int *flags, char *data)
 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 @@ struct super_block *hpfs_read_super(struct super_block *s, void *options,
        }
 
        /*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;
index ed57f59da13e881d2d3efa5fcfb353fb88cfcafd..b1164e3b1f89e080dfb795032bac027cd5cdeebd 100644 (file)
@@ -469,7 +469,6 @@ static unsigned int isofs_get_last_session(struct super_block *sb,s32 session )
 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_iput:
 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",
index cf381d4fad0f4fde8e9de7a88791bffe69ee25fd..49b7b35928b093c2c8a23fa3db4f80b99a366da4 100644 (file)
@@ -774,7 +774,7 @@ journal_t * journal_init_inode (struct inode *inode)
        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);
 
index a348ba9f2d256b8d5561005c80362347e970dd57..67342dc52596a9a8e3bc32a6bdda4b1b390d9cba 100644 (file)
@@ -79,7 +79,7 @@ jffs_read_super(struct super_block *sb, void *data, int silent)
        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 @@ jffs_read_super(struct super_block *sb, void *data, int silent)
        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 @@ jffs_read_super(struct super_block *sb, void *data, int silent)
        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_sb_err2:
        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 @@ static void
 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_put_super(struct super_block *sb)
 
        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));
 }
 
 
index ef79d201282bb99c3c6f8885d66a72684d23ff07..7e9e7bcbc49b6784e571bc45bf009124b1b8f830 100644 (file)
@@ -197,7 +197,7 @@ static struct super_block *jffs2_read_super(struct super_block *sb, void *data,
        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)
index 6347bb16996d10b1ff061e2f612ac0a1502747e0..9d273604a2f80b214b439d9ff03a8060546798fe 100644 (file)
@@ -74,7 +74,7 @@ void minix_free_block(struct inode * inode, int block)
        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 @@ minix_V1_raw_inode(struct super_block *sb, ino_t ino, struct buffer_head **bh)
 
        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 @@ minix_V2_raw_inode(struct super_block *sb, ino_t ino, struct buffer_head **bh)
        *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--;
index 42fd4f97ceb26dbfa1168070df79aee8fb9b88e2..7cd92b2d6ec6fe040ce4d4634b1ac85d9d20f370 100644 (file)
@@ -258,7 +258,7 @@ out_no_map:
 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 @@ int minix_sync_inode(struct inode * inode)
                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;
                }
        }
index 08c972763a7578801f547381f61afc5405de1d18..f4423fd21c1b1af141f4c8d84edf89957e587e60 100644 (file)
@@ -643,8 +643,8 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode)
        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 @@ static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rde
        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 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        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 @@ static int nfs_rmdir(struct inode *dir, struct dentry *dentry)
 {
        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 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry)
 {
        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 @@ nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
        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;
index edbd649fad107da0b008413ba286d734d8cc7dec..1d5e7dbde28af3e137dc661590ca7c61aca52fa4 100644 (file)
@@ -73,8 +73,7 @@ nfs_file_flush(struct file *file)
        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 @@ nfs_fsync(struct file *file, struct dentry *dentry, int datasync)
        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 @@ nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
        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);
 
index 73029044c724a745d1b2bef656d1e288e6df2c42..a96bfbbb54a2ecc94b5af89992b93d15ee957632 100644 (file)
@@ -127,7 +127,7 @@ nfs_write_inode(struct inode *inode, int sync)
 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 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
        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 @@ __nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
                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 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
        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 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
 
        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 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
 
        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 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
        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 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
         */
        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 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
         */
        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;
        }
index a2373467d5e4320d5a16ab97229423a4652af528..617f18282c92ee16b76e463be74742f92301adb9 100644 (file)
@@ -334,7 +334,7 @@ int __init root_nfs_init(void)
  */
 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';
index a7bca2f68a41a9fd373563300463a720f4abcce7..936ce1dba3238b6d9e25d325e1d912d2fa6a4a95 100644 (file)
@@ -108,9 +108,9 @@ nfs_readpage_sync(struct file *file, struct inode *inode, struct page *page)
                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 @@ nfs_pagein_one(struct list_head *head, struct inode *inode)
        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 @@ nfs_readpage_result(struct rpc_task *task)
                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));
index 83f616b739dfd6b64ec152da252cf9d87fe1955c..096ea12f8c2308f89b7dd28b02291b6a8847cae2 100644 (file)
@@ -159,8 +159,8 @@ nfs_writepage_sync(struct file *file, struct inode *inode, struct page *page,
        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 @@ nfs_flush_one(struct list_head *head, struct inode *inode, int how)
        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 @@ nfs_writeback_done(struct rpc_task *task)
 
                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 @@ nfs_commit_done(struct rpc_task *task)
                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));
index 61de7f971a5afbd4ad7ee19fe3f71210253bdb19..93309035fa05d9a97ec6ca7e3dbd52e74797aa8e 100644 (file)
@@ -1245,7 +1245,7 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
        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;
index d490f2553c63955f42eeda5a0905a699108ddc38..025c09cc83f30804889728a3729ab63c5355c867 100644 (file)
@@ -200,7 +200,7 @@ int ntfs_getput_clusters(ntfs_volume *vol, int cluster, ntfs_size_t start_offs,
                                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;
index cccf3980546e1ed404c556a3f48cd11ec4f9a497..fd0f1f862153e172ff43924f7942511e3d3feb7c 100644 (file)
@@ -261,7 +261,7 @@ adfspart_check_ADFS(struct gendisk *hd, struct block_device *bdev,
        /*
         * 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;
index 9bb25fe215711b453296cee453d675e512b41ca9..b74d341c2fa366fcafb9b219edea6b204d06cbf6 100644 (file)
@@ -48,7 +48,7 @@ int qnx4_sync_inode(struct inode *inode)
                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 @@ static void qnx4_write_inode(struct inode *inode, int unused)
        }
        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 @@ static void qnx4_write_inode(struct inode *inode, int unused)
        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 @@ static const char *qnx4_checkroot(struct super_block *sb)
        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 @@ static void qnx4_read_inode(struct inode *inode)
        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 @@ static void qnx4_read_inode(struct inode *inode)
        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 @@ static void qnx4_read_inode(struct inode *inode)
                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);
 }
 
index 166f99269c9eac3b4559ca56fb20c8e7fff0461a..f282935b77987e7616477a37daa3e0d174f8ddf5 100644 (file)
@@ -213,7 +213,7 @@ int qnx4_unlink(struct inode *dir, struct dentry *dentry)
        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;
        }
index 05afd10d949e3a145d6a735cb85939dbec6649b4..9caf7329858686d01be18b0ce2d32087e0ce91cf 100644 (file)
@@ -111,8 +111,8 @@ void reiserfs_free_block (struct reiserfs_transaction_handle *th, unsigned long
   /* 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]);
 
index 9a398e3d05b626369f8f90dae1568e55ee99143c..2dec58fa9c973f67e231d9b1d876acd0266e2941 100644 (file)
@@ -2115,7 +2115,7 @@ static void tb_buffer_sanity_check (struct super_block * p_s_sb,
       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);
index 24dbf3ba76be72be74108837d056a9ebf4a9506d..688d5c8e6de9c5ce226973a4ea61105a7d47b69e 100644 (file)
@@ -965,7 +965,7 @@ static void inode2sd (void * sd, struct inode * inode)
     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 @@ static void inode2sd_v1 (void * sd, struct inode * inode)
     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 @@ struct inode * reiserfs_new_inode (struct reiserfs_transaction_handle *th,
     // 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 @@ research:
            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 ;
     }
index 3b70c989bd708ee322560f618e641eb086e5d479..7bef738e2b0e54f3e75d92fe8e443059fb122003 100644 (file)
@@ -420,7 +420,7 @@ static inline struct reiserfs_journal_cnode *get_journal_hash_dev(struct reiserf
   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 @@ static struct reiserfs_journal_list *find_newer_jl_for_cn(struct reiserfs_journa
 
   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 @@ loop_start:
                 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 @@ void remove_journal_hash(struct reiserfs_journal_cnode **table, struct reiserfs_
   }
   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 @@ void remove_journal_hash(struct reiserfs_journal_cnode **table, struct reiserfs_
        *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 @@ static int journal_read(struct super_block *p_s_sb) {
   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 @@ static int can_dirty(struct reiserfs_journal_cnode *cn) {
   ** 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 @@ static int can_dirty(struct reiserfs_journal_cnode *cn) {
   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 @@ int journal_mark_freed(struct reiserfs_transaction_handle *th, struct super_bloc
     /* 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) {
index c601d82cbffe2ad57575eb88ed3f66b61f8913e5..487975385609261bc75540339db4f3ab533c49da 100644 (file)
@@ -817,7 +817,7 @@ int reiserfs_unlink (struct inode * dir, struct dentry *dentry)
   
     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;
     }
 
index 5fce65fec01387d1b66262a732432a786a621ee8..987ee487f8305975183686048c1dd6dac390cab5 100644 (file)
@@ -335,7 +335,7 @@ void reiserfs_panic (struct super_block * sb, const char * fmt, ...)
 
   /* 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);
 }
 
 
index 3b1d6073a9624a67144bf97e91dc79a885961bdb..5e6a45b69afa9df8a2b2519e44e766109bb5ea8b 100644 (file)
@@ -546,14 +546,13 @@ static const char *proc_info_root_name = "fs/reiserfs";
 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 @@ int reiserfs_proc_info_done( struct super_block *sb )
        __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;
index 1fc6df0f5548adb103f5be5be5fe1c3ca7816675..cbdc1a349599efb75f87eebd9db6b599c4c6007c 100644 (file)
@@ -395,7 +395,7 @@ static  inline  int key_in_buffer (
          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 )
index 9a10a9b15425072b55aa771938f13d0544fb0cf2..4281c39ee0b0b5d42d0b10e04361943b13f9d862 100644 (file)
@@ -221,7 +221,7 @@ static int parse_options (char * options, unsigned long * mount_options, unsigne
 
 
 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 @@ static int read_super_block (struct super_block * s, int offset)
     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 @@ static int read_super_block (struct super_block * s, int offset)
     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 @@ static int read_super_block (struct super_block * s, int offset)
     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 @@ static int read_super_block (struct super_block * s, int offset)
     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
index afe762afe224519f32dbb5becff8b70b231ca641..0aa6ca89bbcc93682feeb43bdc375e354bad8d77 100644 (file)
@@ -96,7 +96,6 @@ static struct super_block *
 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 @@ romfs_read_super(struct super_block *s, void *data, int silent)
           || 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;
        }
 
index fd59a186c3bf9989a0b86466e2266cd531e37b57..14f9a9a9a70efb35fff608e15a379f74e78574c8 100644 (file)
@@ -718,16 +718,17 @@ restart:
        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);
index 474e67ec6501b4389f1fb966d915a69551ef10cc..4e20e5e9844c930621c05a1c3bd9148b8314965a 100644 (file)
@@ -109,7 +109,7 @@ void sysv_free_inode(struct inode * inode)
        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);
index d1ee3e89567d699982e6b662543bc37f4214c998..15b8b321bde4c6685409fb4aadf2dec33e214238 100644 (file)
@@ -149,15 +149,14 @@ static void sysv_read_inode(struct inode *inode)
 
        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 @@ static struct buffer_head * sysv_update_inode(struct inode * inode)
        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 @@ int sysv_sync_inode(struct inode * inode)
                 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;
                 }
         }
index 3f856ead1dfc9fd060c6e1fc2ec8f5640d36267b..7c9c82932e7b4317f60e36d87fae78b47e33fa10 100644 (file)
@@ -208,7 +208,7 @@ static int detect_sysv (struct super_block *sb, struct buffer_head *bh)
                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 @@ static int detect_sysv (struct super_block *sb, struct buffer_head *bh)
 
        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 @@ static int complete_read_super(struct super_block *sb, int silent, int size)
 
        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 @@ static struct super_block *sysv_read_super(struct super_block *sb,
 {
        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 @@ Eunknown:
        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 @@ static struct super_block *v7_read_super(struct super_block *sb,void *data,
                                  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 @@ static struct super_block *v7_read_super(struct super_block *sb,void *data,
        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;
        }
 
index f8a1806026b6da094fd82b2b8f134d8ef3118ca2..edd94ec5b74ee5d13684a161312d5d855b350bcd 100644 (file)
@@ -1531,7 +1531,7 @@ udf_update_inode(struct inode *inode, int do_sync)
                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;
                }
        }
index 942f5536422850eb700bfc5740de1272bdb52ec0..7a06f1d276b5efa117c35878b02ae0749c9f43b4 100644 (file)
@@ -1549,7 +1549,7 @@ void udf_error(struct super_block *sb, const char *function,
        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 @@ void udf_warning(struct super_block *sb, const char *function,
        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);
 }
 
 /*
index 309f4b656eff0203729ddd2f8d072534433f5973..8d1260c601cc7a0361ef5285b003977a574617fb 100644 (file)
@@ -203,13 +203,13 @@ void ufs_error (struct super_block * sb, const char * function,
        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 @@ void ufs_panic (struct super_block * sb, const char * function,
        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 @@ void ufs_warning (struct super_block * sb, const char * function,
        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)
index 7964e1490e8dab74677fa51563afde256c2d78ee..a83af6000e23412aa43b1a9241495371872b7cb0 100644 (file)
@@ -718,6 +718,8 @@ struct super_block {
        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;
index fa98ce32a6e7d758bb3d201b6c894f9717efba71..ab6c834d0f315882eaf69741799323cb74e1f6f2 100644 (file)
@@ -122,7 +122,7 @@ static inline void hfs_mdb_dirty(hfs_sysmdb sys_mdb) {
 }
 
 static inline const char *hfs_mdb_name(hfs_sysmdb sys_mdb) {
-       return kdevname(sys_mdb->s_dev);
+       return sys_mdb->s_id;
 }
 
 
index 4ae1b21703c419aa8935e4a3c2220f4e5287680c..b50d60989eae33771bdb31c9a0770e578407cf42 100644 (file)
@@ -1651,7 +1651,7 @@ extern wait_queue_head_t reiserfs_commit_thread_wait ;
 #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
index 172e0918ec5ec400d8fc1af55c7deb517000c575..6194ad03b5ffb8f6cba67d6016bb70515ce72a5f 100644 (file)
@@ -460,6 +460,7 @@ struct usb_device_id {
 
 /**
  * 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 @@ struct usb_device_id {
  * well as cancel any I/O requests that are still pending.
  */
 struct usb_driver {
+       struct module *owner;
        const char *name;
 
        void *(*probe)(
index 925ea33ee3823cf74dda32d7229c2d4969b50c40..d954d493e13d2385ed4ba393a07f74a72359b4fb 100644 (file)
@@ -631,7 +631,7 @@ static int __init rd_load_disk(int n)
 #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 devfs_make_root(char *name)
 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 @@ static void __init mount_root(void)
                        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 int do_linuxrc(void * shell)
 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 @@ static void __init handle_initrd(void)
        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 void __init handle_initrd(void)
 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 @@ void prepare_namespace(void)
 #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);
index f7cf77cedaf99f7ac28e8c950939922002fee7cb..dc5eea2af91025f8cd7fa4b2e5f827e04bb6c172 100644 (file)
@@ -54,8 +54,8 @@ extern void mem_use(void);
  * 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 @@ void expire_task(struct task_struct *p)
                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;
+       }
 }
 
 /*
index b790182f5be64b633d15b5cfe69db10d3afb0130..923d7f362622ba5fe9ad713c8054f9c9ae8ae633 100644 (file)
@@ -1145,7 +1145,7 @@ static int __init ip_auto_config(void)
         */
        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 @@ static int __init ip_auto_config(void)
                         *                              -- 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;