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

diff -urN linux-2.4.18-pre2/Documentation/Configure.help linux/Documentation/Configure.help
--- linux-2.4.18-pre2/Documentation/Configure.help	Thu Jan 10 13:28:30 2002
+++ linux/Documentation/Configure.help	Thu Jan 10 13:28:32 2002
@@ -15671,13 +15671,10 @@
 
 Support for PMU-based PowerMacs
 CONFIG_ADB_PMU
-  On the PowerBook 3400 and 2400, the PMU is a 6805 microprocessor
-  core whose primary function is to control battery charging and
-  system power.  The PMU also controls the ADB (Apple Desktop Bus)
-  which connects to the keyboard and mouse, as well as the
-  non-volatile RAM and the RTC (real time clock) chip.  Say Y to
-  enable support for this device; you should do so if your machine
-  is one of these PowerBooks.
+  This provides support for PMU based Power Macintosh systems.  This
+  includes all PowerBooks and all AGP-based machines.
+  
+  If unsure say Y.
 
 Include MacIO ADB driver
 CONFIG_ADB_MACIO
@@ -20468,7 +20465,7 @@
   <file:Documentation/modules.txt>.
 
 PowerMac DMA sound support
-CONFIG_DMASOUND_AWACS
+CONFIG_DMASOUND_PMAC
   If you want to use the internal audio of your PowerMac in Linux,
   answer Y to this question. This will provide a Sun-like /dev/audio,
   compatible with the Linux/i386 sound system. Otherwise, say N.
@@ -20728,6 +20725,14 @@
   have it autoloaded. The act of removing the module shuts down the
   sound hardware for more power savings.
 
+APM emulation
+CONFIG_PMAC_APM_EMU
+  This driver provides an emulated /dev/apm_bios and /proc/apm. The
+  first one is mostly intended for XFree to sleep & wakeup properly,
+  the second ones provides some battery informations to allow existing
+  APM utilities to work. It provides less useful informations than
+  tools specifically designed for PowerBooks or /proc/pmu/battery_x
+
 Backlight control for LCD screens
 CONFIG_PMAC_BACKLIGHT
   Say Y here to build in code to manage the LCD backlight on a
@@ -20806,7 +20811,7 @@
     End of life: mid 2001 (?)
     URL: <http://www.tz-mikroelektronik.de/ServiceModule/index.html>
 
-  HERMES:
+  HERMES_PRO:
     Hermes-Pro ISDN/LAN router with integrated 8 x hub
     Manufacturer: Multidata Gesellschaft für Datentechnik und Informatik
       <http://www.multidata.de/>
diff -urN linux-2.4.18-pre2/Documentation/usb/ov511.txt linux/Documentation/usb/ov511.txt
--- linux-2.4.18-pre2/Documentation/usb/ov511.txt	Sun Nov 12 20:45:18 2000
+++ linux/Documentation/usb/ov511.txt	Thu Jan 10 13:28:32 2002
@@ -8,11 +8,11 @@
 INTRODUCTION:
 
 This is a driver for the OV511, a USB-only chip used in many "webcam" devices.
-Any camera using the OV511/OV511+ and the OV7610/20/20AE CCD should work. It 
+Any camera using the OV511/OV511+ and the OV6620/OV7610/20/20AE should work.
+Video capture devices that use the Philips SAA7111A decoder also work. It 
 supports streaming and capture of color or monochrome video via the Video4Linux
-API. Most V4L apps are compatible with it, but a few video-conferencing programs
-do not work yet. The following resolutions are supported: 640x480, 448x336,
-384x288, 352x288, and 320x240.
+API. Most V4L apps are compatible with it. Most resolutions with a width and
+height that are a multiple of 8 are supported.
 
 If you need more information, please visit the OV511 homepage at the above URL.
 
@@ -27,22 +27,25 @@
 
 HOW TO USE IT:
 
+Note: These are simplified instructions. For complete instructions see:
+	http://alpha.dyndns.org/ov511/install.html
+
 You must have first compiled USB support, support for your specific USB host
 controller (UHCI or OHCI), and Video4Linux support for your kernel (I recommend
-making them modules.)
+making them modules.) Make sure "Enforce bandwidth allocation" is NOT enabled.
 
-Next, (as root) from your appropriate modules directory (lib/modules/2.3.XX):
+Next, (as root):
 
-	insmod usb/usbcore.o
-	insmod usb/usb-uhci.o  <OR>  insmod usb/ohci-hcd.o
-	insmod misc/videodev.o
-	insmod usb/ov511.o
+	modprobe usbcore
+	modprobe usb-uhci  <OR>  modprobe usb-ohci
+	modprobe videodev
+	modprobe ov511
 
 If it is not already there (it usually is), create the video device:
 
-	mknod /dev/video c 81 0
+	mknod /dev/video0 c 81 0
 
-Sometimes /dev/video is a symlink to /dev/video0
+Optionally, symlink /dev/video to /dev/video0
 
 You will have to set permissions on this device to allow you to read/write
 from it:
@@ -55,39 +58,40 @@
 	
 [Using vidcat:]
 
-	vidcat -s 640x480 > test.jpg
+	vidcat -s 640x480 -p c > test.jpg
 	xview test.jpg
 	
 [Using xawtv:]
 
-You must make some modifications to the source and compile it before you use it.
-(Note: this may not be applicable to versions other than 3.06)
-
-In src/Xawtv.ad, change xawtv.tv.width to 640 and xawtv.tv.height to 480. Next,
-in src/grab-v4l.c, change SYNC_TIMEOUT from 1 to 2. Then, from the main xawtv
-directory:
+From the main xawtv directory:
 
 	make clean
 	./configure
 	make
 	make install
 
-Now you should be able to run xawtv. Right click for the options dialog. If
-you get a scrambled image it is likely that you made a mistake in Xawtv.ad.
-Try setting the size to 320x240 if all else fails.
+Now you should be able to run xawtv. Right click for the options dialog. 
 
 MODULE PARAMETERS:
 
   You can set these with:  insmod ov511 NAME=VALUE
   There is currently no way to set these on a per-camera basis.
 
-  NAME: autoadjust
-  TYPE: integer (boolean)
+  NAME: autobright
+  TYPE: integer (Boolean)
   DEFAULT: 1
-  DESC: The camera normally adjusts exposure, gain, and hue automatically. This
-        can be set to 0 to disable this automatic adjustment. Note that there is
-        currently no way to set these parameters manually once autoadjust is
-        disabled.
+  DESC: Brightness is normally under automatic control and can't be set
+        manually by the video app. Set to 0 for manual control.
+
+  NAME: autogain
+  TYPE: integer (Boolean)
+  DEFAULT: 1
+  DESC: Auto Gain Control enable. This feature is not yet implemented.
+
+  NAME: autoexp
+  TYPE: integer (Boolean)
+  DEFAULT: 1
+  DESC: Auto Exposure Control enable. This feature is not yet implemented.
 
   NAME: debug
   TYPE: integer (0-6)
@@ -102,49 +106,23 @@
           5=highly repetitive mesgs
 
   NAME: fix_rgb_offset
-  TYPE: integer (boolean)
+  TYPE: integer (Boolean)
   DEFAULT: 0
   DESC: Some people have reported that the blue component of the image is one
         or so lines higher than the red component. This is only apparent in 
         images with white objects on black backgrounds at 640x480. Setting this
-        to 1 will realign the color planes correctly. NOTE: This is still
-        experimental and very buggy. You will likely need a fast (500 MHz) CPU.
+        to 1 will realign the color planes correctly. NOTE: You will likely
+        need a fast (500 MHz) CPU.
 
   NAME: snapshot
-  TYPE: integer (boolean)
+  TYPE: integer (Boolean)
   DEFAULT: 0
-  DESC: Set to 1 to enable snapshot mode. read() will block until the snapshot
-        button is pressed. Note that this does not yet work with most apps,
-        including xawtv and vidcat. NOTE: See the section "TODO" for more info.
-
-  NAME: sensor
-  TYPE: integer ([0, 1, 3])
-  DEFAULT: [varies]
-  DESC: If you know that your camera sensor is not detected correctly, set this
-        parameter. This is a global option for all attached OV511 cameras. You
-        will probably never need to set this, but if you do, valid values are:
-        	0 for OV7620
-        	1 for OV7620AE
-        	3 for OV7610
-
-  NAME: i2c_detect_tries
-  TYPE: integer (don't set it insanely high!)
-  DEFAULT: 5
-  DESC: This is the number of times the driver will try to sync and detect the
-        internal i2c bus (which connects the OV511 and sensor). If you are
-        getting intermittent detection failures ("Failed to read sensor ID...")
-        you should increase this by a modest amount. If setting it to 20 or so
-        doesn't fix things, look elsewhere for the cause of the problem.
-
-  NAME: aperture
-  TYPE: integer (0 - 15)
-  DEFAULT: [varies by sensor]
-  DESC: For legal values, see the OV7610/7620 specs under register Common F.
-        This setting affects the upper nybble of that reg (bits 4-7). This is
-        for if you want to play with the camera's pixel saturation.
+  DESC: Set to 1 to enable snapshot mode. read()/VIDIOCSYNC will block until
+	the snapshot button is pressed. Note: enabling this mode disables
+	/proc/video/ov511/<minor#>/button
 
-  NAME: force_rgb
-  TYPE: integer (boolean)
+  NAME: force_rgb	(Deprecated; may be removed in the future)
+  TYPE: integer (Boolean)
   DEFAULT: 0
   DESC: Force image to be read in RGB instead of BGR. This option allow
         programs that expect RGB data (e.g. gqcam) to work with this driver. If
@@ -169,60 +147,179 @@
         both OV511 and OV511+ cameras, trial-and-error may be necessary for
         finding the optimum setting.
 
-  NAME: retry_sync
-  TYPE: boolean
+  NAME: compress
+  TYPE: integer (Boolean)
+  DEFAULT: 0
+  DESC: Set this to 1 to turn on the camera's compression engine. This can
+        potentially increase the frame rate at the expense of quality, if you
+        have a fast CPU. You must load the proper compression module for your
+        camera before starting your application (ov511_decomp or ov518_decomp).
+
+  NAME: testpat
+  TYPE: integer (Boolean)
   DEFAULT: 0
-  DESC: Prevent apps from timing out if frame is not done in time. This is
-        useful if you are having problems with Xawtv getting "stuck" on a frame
-        when your system is under heavy load.
+  DESC: This configures the camera's sensor to transmit a colored test-pattern
+        instead of an image. This does not work correctly yet.
 
-  NAME: sensor_gbr
-  TYPE: boolean
+  NAME: sensor_gbr (*** TEMPORARILY DISABLED ***)
+  TYPE: integer (Boolean)
   DEFAULT: 0
   DESC: This makes the sensor output GBR422 instead of YUV420. This saves the
         driver the trouble of converting YUV to RGB, but it currently does not
         work very well (the colors are not quite right)
 
+  NAME: dumppix
+  TYPE: integer (0-2)
+  DEFAULT: 0
+  DESC: Dumps raw pixel data and skips post-processing and format conversion.
+	It is for debugging purposes only. Options are:
+		0: Disable (default)
+		1: Dump raw data from camera, excluding headers and trailers
+		2: Dumps data exactly as received from camera
+
+  NAME: led
+  TYPE: integer (0-2)
+  DEFAULT: 1 (Always on)
+  DESC: Controls whether the LED (the little light) on the front of the camera
+	is always off (0), always on (1), or only on when driver is open (2).
+	This is only supported with the OV511+ chipset, and even then only on
+	some cameras (ones that actually have the LED wired to the control pin,
+	and not just hardwired to be on all the time).
+
+  NAME: dump_bridge
+  TYPE: integer (Boolean)
+  DEFAULT: 0
+  DESC: Dumps the bridge (OV511[+] or OV518[+]) register values to the system
+	log. Only useful for serious debugging/development purposes.
+
+  NAME: dump_sensor
+  TYPE: integer (Boolean)
+  DEFAULT: 0
+  DESC: Dumps the sensor register values to the system log. Only useful for
+	serious debugging/development purposes.
+
+  NAME: printph
+  TYPE: integer (Boolean)
+  DEFAULT: 0
+  DESC: Setting this to 1 will dump the first 12 bytes of each isoc frame. This
+	is only useful if you are trying to debug problems with the isoc data
+	stream (i.e.: camera initializes, but vidcat hangs until Ctrl-C). Be
+	warned that this dumps a large number of messages to your kernel log.
+
+  NAME: phy, phuv, pvy, pvuv, qhy, qhuv, qvy, qvuv
+  TYPE: integer (0-63 for phy and phuv, 0-255 for rest)
+  DEFAULT: OV511 default values
+  DESC: These are registers 70h - 77h of the OV511, which control the
+	prediction ranges and quantization thresholds of the compressor, for
+	the Y and UV channels in the horizontal and vertical directions. See
+	the OV511 or OV511+ data sheet for more detailed descriptions. These
+	normally do not need to be changed.
+
+  NAME: lightfreq
+  TYPE: integer (0, 50, or 60)
+  DEFAULT: 0 (use sensor default)
+  DESC: Sets the sensor to match your lighting frequency. This can reduce the
+	appearance of "banding", i.e. horizontal lines or waves of light and
+	dark that are often caused by artificial lighting. Valid values are:
+		0 - Use default (depends on sensor, most likely 60 Hz)
+		50 - For European and Asian 50 Hz power
+		60 - For American 60 Hz power
+
+  NAME: bandingfilter
+  TYPE: integer (Boolean)
+  DEFAULT: 0 (off)
+  DESC: Enables the sensor´s banding filter exposure algorithm. This reduces
+	or stabilizes the "banding" caused by some artificial light sources
+	(especially fluorescent). You might have to set lightfreq correctly for
+	this to work right. As an added bonus, this sometimes makes it
+	possible to capture your monitor´s output.
+
+  NAME: fastset
+  TYPE: integer (Boolean)
+  DEFAULT: 0 (off)
+  DESC: Allows picture settings (brightness, contrast, color, and hue) to take
+	effect immediately, even in the middle of a frame. This reduces the
+	time to change settings, but can ruin frames during the change. Only
+	affects OmniVision sensors.
+
+  NAME: force_palette
+  TYPE: integer (Boolean)
+  DEFAULT: 0 (off)
+  DESC: Forces the palette (color format) to a specific value. If an
+	application requests a different palette, it will be rejected, thereby
+	forcing it to try others until it succeeds. This is useful for forcing
+	greyscale mode with a color camera, for example. Supported modes are:
+		0                           (Allows all the following formats)
+		1   VIDEO_PALETTE_GREY      (Linear greyscale)
+		3   VIDEO_PALETTE_RGB565    (565 16 bit RGB)
+		4   VIDEO_PALETTE_RGB24     (24bit RGB)
+		7   VIDEO_PALETTE_YUV422    (YUV422 capture)
+		8   VIDEO_PALETTE_YUYV      (YUV422 capture; same as 7)
+		10  VIDEO_PALETTE_YUV420    (YUV 4:2:0 Planar)
+		13  VIDEO_PALETTE_YUV422P   (YUV 4:2:2 Planar)
+		15  VIDEO_PALETTE_YUV420P   (YUV 4:2:0 Planar, same as 10)
+
+  NAME: tuner
+  TYPE: integer
+  DEFAULT: -1 (autodetect)
+  DESC: This sets the exact type of the tuner module in a device. This is set
+	automatically based on the custom ID of the OV511 device. In cases
+	where this fails, you can override this auto-detection. Please see
+	linux/drivers/media/video/tuner.h for a complete list.
+
+  NAME: backlight
+  TYPE: integer (Boolean)
+  DEFAULT: 0 (off)
+  DESC: Setting this flag changes the exposure algorithm for OmniVision sensors
+	such that objects in the camera's view (i.e. your head) can be clearly
+	seen when they are illuminated from behind. It reduces or eliminates
+	the sensor's auto-exposure function, so it should only be used when
+	needed. Additionally, it is only supported with the OV6620 and OV7620.
+
+  NAME: unit_video
+  TYPE: Up to 16 comma-separated integers
+  DEFAULT: 0,0,0... (automatically assign the next available minor(s))
+  DESC: You can specify up to 16 minor numbers to be assigned to ov511 devices.
+	For example, "unit_video=1,3" will make the driver use /dev/video1 and
+	/dev/video3 for the first two devices it detects. Additional devices
+	will be assigned automatically starting at the first available device
+	node (/dev/video0 in this case). Note that you cannot specify 0 as a
+	minor number. This feature requires kernel version 2.4.5 or higher.
+
+  NAME: remove_zeros
+  TYPE: integer (Boolean)
+  DEFAULT: 0 (do not skip any incoming data)
+  DESC: Setting this to 1 will remove zero-padding from incoming data. This
+	will compensate for the blocks of corruption that can appear when the
+	camera cannot keep up with the speed of the USB bus (eg. at low frame
+	resolutions). This feature is always enabled when compression is on.
+ 
 WORKING FEATURES:
- o Color streaming/capture at 640x480, 448x336, 384x288, 352x288, and 320x240
- o RGB24, RGB565, YUV420, YUV422, YUYV, and YUV422P color
- o Monochrome
+ o Color streaming/capture at most widths and heights that are multiples of 8.
+ o RGB24, RGB565, YUV420/YUV420P, YUV422/YUYV, and YUV422P color
+ o Monochrome (use force_palette=1 to enable)
  o Setting/getting of saturation, contrast, brightness, and hue (only some of
    them work the OV7620 and OV7620AE)
  o /proc status reporting
+ o SAA7111A video capture support at 320x240 and 640x480
+ o Compression support
 
 EXPERIMENTAL FEATURES:
- o fix_rgb_offset: Sometimes works, but other times causes errors with xawtv and
-   corrupted frames. If you have a very fast CPU, you can try it.
- o Snapshot mode (only works with some read() based apps; see below for more)
- o OV6620 sensor support
- o GBR422 parsing
- o 160x120
-
-TODO:
- o Fix the noise / grainy image problem.
- o Get compression working. It would be a nice addition as it improves
-   frame rate quite a bit. OmniVision wouldn't tell me how the algorithm works,
-   so we can't really work on that yet. Please kindly inform OmniVision that you
-   would like them to release their specifications to the Linux community.
- o YUV422
- o Fix fixFrameRGBoffset(). It is not stable yet with streaming video.
- o V4L2 support (Probably not until it goes into the kernel)
- o Get rid of the memory management functions (put them in videodev.c??)
- o Setting of contrast and brightness not working with 7620/7620AE
- o Driver/camera state save/restore for when USB supports suspend/resume
- o Unstable on SMP systems
- o OV7620/OV6620 experience frame corruption with moving objects
- o OV6620 is too dark
- o 176x144 support
- o Driver sometimes hangs upon close() with OHCI
- o The image should always be written properly to the mmap'ed buffer as long as
-   the requested image size is at least the minimum size. This will likely
-   require a rewrite of all the parsing code.
+ o OV6630 sensor support
+ o Banding filter
+ o SMP compatibility
+
+TO-DO:
+ o V4L2 support (This will be done after the next kernel patch release)
+ o Setting of hue not working with OV7620
+ o Setting of contrast and hue not working with OV7620AE
+ o OV8600 sensor support (Not used in anything yet)
+ o OV518/OV518+ support (all that's needed is the decompressor)
+ o cams >= 3 not working
 
 HOW TO CONTACT ME:
 
-You can email me at mwm@i.am . Please prefix the subject line
+You can email me at mmcclell@bigfoot.com . Please prefix the subject line
 with "OV511: " so that I am certain to notice your message.
 
 CREDITS:
@@ -232,3 +329,4 @@
 and the USB stack. Thanks to Bret Wallach for getting camera reg IO, ISOC, and
 image capture working. Thanks to Orion Sky Lawlor, Kevin Moore, and Claudio
 Matsuoka for their work as well.
+
diff -urN linux-2.4.18-pre2/Makefile linux/Makefile
--- linux-2.4.18-pre2/Makefile	Thu Jan 10 13:28:30 2002
+++ linux/Makefile	Thu Jan 10 13:28:32 2002
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 4
 SUBLEVEL = 18
-EXTRAVERSION = pre2
+EXTRAVERSION = -pre3
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
@@ -137,7 +137,8 @@
 	drivers/net/net.o \
 	drivers/media/media.o
 DRIVERS-$(CONFIG_AGP) += drivers/char/agp/agp.o
-DRIVERS-$(CONFIG_DRM) += drivers/char/drm/drm.o
+DRIVERS-$(CONFIG_DRM_NEW) += drivers/char/drm/drm.o
+DRIVERS-$(CONFIG_DRM_OLD) += drivers/char/drm-4.0/drm.o
 DRIVERS-$(CONFIG_NUBUS) += drivers/nubus/nubus.a
 DRIVERS-$(CONFIG_ISDN) += drivers/isdn/isdn.a
 DRIVERS-$(CONFIG_NET_FC) += drivers/net/fc/fc.o
@@ -329,11 +330,13 @@
 	@echo '#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))' >>.ver
 	@mv -f .ver $@
 
+comma	:= ,
+
 init/version.o: init/version.c include/linux/compile.h include/config/MARKER
-	$(CC) $(CFLAGS) $(CFLAGS_KERNEL) -DUTS_MACHINE='"$(ARCH)"' -c -o init/version.o init/version.c
+	$(CC) $(CFLAGS) $(CFLAGS_KERNEL) -DUTS_MACHINE='"$(ARCH)"' -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) -c -o init/version.o init/version.c
 
 init/main.o: init/main.c include/config/MARKER
-	$(CC) $(CFLAGS) $(CFLAGS_KERNEL) $(PROFILING) -c -o $*.o $<
+	$(CC) $(CFLAGS) $(CFLAGS_KERNEL) $(PROFILING) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) -c -o $*.o $<
 
 fs lib mm ipc kernel drivers net: dummy
 	$(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" $(subst $@, _dir_$@, $@)
diff -urN linux-2.4.18-pre2/Rules.make linux/Rules.make
--- linux-2.4.18-pre2/Rules.make	Tue Mar  6 19:31:01 2001
+++ linux/Rules.make	Thu Jan 10 13:28:32 2002
@@ -31,6 +31,8 @@
 unexport subdir-n
 unexport subdir-
 
+comma	:= ,
+
 #
 # Get things started.
 #
@@ -54,7 +56,7 @@
 	$(CPP) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) $< > $@
 
 %.o: %.c
-	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -c -o $@ $<
+	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) $(CFLAGS_$@) -c -o $@ $<
 	@ ( \
 	    echo 'ifeq ($(strip $(subst $(comma),:,$(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@))),$$(strip $$(subst $$(comma),:,$$(CFLAGS) $$(EXTRA_CFLAGS) $$(CFLAGS_$@))))' ; \
 	    echo 'FILES_FLAGS_UP_TO_DATE += $@' ; \
@@ -270,7 +272,7 @@
 
 ifneq "$(strip $(export-objs))" ""
 $(export-objs): $(export-objs:.o=.c) $(TOPDIR)/include/linux/modversions.h
-	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -DEXPORT_SYMTAB -c $(@:.o=.c)
+	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) $(CFLAGS_$@) -DEXPORT_SYMTAB -c $(@:.o=.c)
 	@ ( \
 	    echo 'ifeq ($(strip $(subst $(comma),:,$(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -DEXPORT_SYMTAB)),$$(strip $$(subst $$(comma),:,$$(CFLAGS) $$(EXTRA_CFLAGS) $$(CFLAGS_$@) -DEXPORT_SYMTAB)))' ; \
 	    echo 'FILES_FLAGS_UP_TO_DATE += $@' ; \
diff -urN linux-2.4.18-pre2/arch/alpha/kernel/pci_iommu.c linux/arch/alpha/kernel/pci_iommu.c
--- linux-2.4.18-pre2/arch/alpha/kernel/pci_iommu.c	Thu Jan 10 13:28:30 2002
+++ linux/arch/alpha/kernel/pci_iommu.c	Thu Jan 10 13:28:32 2002
@@ -30,6 +30,10 @@
 #define DEBUG_NODIRECT 0
 #define DEBUG_FORCEDAC 0
 
+/* Most Alphas support 32-bit ISA DMA. Exceptions are XL, Ruffian and
+   Nautilus (see asm/dma.h for details). */
+#define ISA_DMA_MASK	(MAX_DMA_ADDRESS - IDENT_ADDR - 1 < 0xffffffff ? \
+			 MAX_DMA_ADDRESS - IDENT_ADDR - 1 : 0xffffffff)
 
 static inline unsigned long
 mk_iommu_pte(unsigned long paddr)
@@ -181,7 +185,7 @@
 		 int dac_allowed)
 {
 	struct pci_controller *hose = pdev ? pdev->sysdata : pci_isa_hose;
-	dma_addr_t max_dma = pdev ? pdev->dma_mask : 0x00ffffff;
+	dma_addr_t max_dma = pdev ? pdev->dma_mask : ISA_DMA_MASK;
 	struct pci_iommu_arena *arena;
 	long npages, dma_ofs, i;
 	unsigned long paddr;
@@ -215,16 +219,8 @@
 	/* 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) {
-		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;
+		printk(KERN_WARNING "pci_map_single failed: no hw sg\n");
+		return 0;
 	}
 		
 	arena = hose->sg_pci;
@@ -593,7 +589,7 @@
 	/* Second, figure out where we're going to map things.  */
 	if (alpha_mv.mv_pci_tbi) {
 		hose = pdev ? pdev->sysdata : pci_isa_hose;
-		max_dma = pdev ? pdev->dma_mask : 0x00ffffff;
+		max_dma = pdev ? pdev->dma_mask : ISA_DMA_MASK;
 		arena = hose->sg_pci;
 		if (!arena || arena->dma_base + arena->size - 1 > max_dma)
 			arena = hose->sg_isa;
@@ -656,7 +652,7 @@
 		return;
 
 	hose = pdev ? pdev->sysdata : pci_isa_hose;
-	max_dma = pdev ? pdev->dma_mask : 0x00ffffff;
+	max_dma = pdev ? pdev->dma_mask : ISA_DMA_MASK;
 	arena = hose->sg_pci;
 	if (!arena || arena->dma_base + arena->size - 1 > max_dma)
 		arena = hose->sg_isa;
diff -urN linux-2.4.18-pre2/arch/arm/kernel/entry-common.S linux/arch/arm/kernel/entry-common.S
--- linux-2.4.18-pre2/arch/arm/kernel/entry-common.S	Thu Oct 11 09:04:57 2001
+++ linux/arch/arm/kernel/entry-common.S	Thu Jan 10 13:28:32 2002
@@ -22,12 +22,10 @@
  * Our do_softirq out of line code.  See include/asm-arm/softirq.h for
  * the calling assembly.
  */
-	.section ".text.lock","ax"
 ENTRY(__do_softirq)
 	stmfd	sp!, {r0 - r3, ip, lr}
 	bl	do_softirq
 	ldmfd	sp!, {r0 - r3, ip, pc}
-	.previous
 
 	.align	5
 /*
diff -urN linux-2.4.18-pre2/arch/arm/kernel/semaphore.c linux/arch/arm/kernel/semaphore.c
--- linux-2.4.18-pre2/arch/arm/kernel/semaphore.c	Fri Apr 27 14:11:59 2001
+++ linux/arch/arm/kernel/semaphore.c	Thu Jan 10 13:28:32 2002
@@ -177,8 +177,7 @@
  * value in some cases..
  */
 #ifdef CONFIG_CPU_26
-asm("	.section	.text.lock, \"ax\"
-	.align	5
+asm("	.align	5
 	.globl	__down_failed
 __down_failed:
 	stmfd	sp!, {r0 - r3, lr}
@@ -212,13 +211,11 @@
 	bl	__up
 	ldmfd	sp!, {r0 - r3, pc}^
 
-	.previous
 	");
 
 #else
 /* 32 bit version */
-asm("	.section	.text.lock, \"ax\"
-	.align	5
+asm("	.align	5
 	.globl	__down_failed
 __down_failed:
 	stmfd	sp!, {r0 - r3, lr}
@@ -252,7 +249,6 @@
 	bl	__up
 	ldmfd	sp!, {r0 - r3, pc}
 
-	.previous
 	");
 
 #endif
diff -urN linux-2.4.18-pre2/arch/arm/vmlinux-armo.lds.in linux/arch/arm/vmlinux-armo.lds.in
--- linux-2.4.18-pre2/arch/arm/vmlinux-armo.lds.in	Sun Aug 12 11:13:59 2001
+++ linux/arch/arm/vmlinux-armo.lds.in	Thu Jan 10 13:28:32 2002
@@ -48,7 +48,6 @@
 			*(.text)
 			*(.fixup)
 			*(.gnu.warning)
-			*(.text.lock)	/* out-of-line lock text */
 			*(.rodata)
 			*(.rodata.*)
 			*(.glue_7)
diff -urN linux-2.4.18-pre2/arch/arm/vmlinux-armv.lds.in linux/arch/arm/vmlinux-armv.lds.in
--- linux-2.4.18-pre2/arch/arm/vmlinux-armv.lds.in	Sun Aug 12 11:13:59 2001
+++ linux/arch/arm/vmlinux-armv.lds.in	Thu Jan 10 13:28:32 2002
@@ -43,7 +43,6 @@
 			*(.text)
 			*(.fixup)
 			*(.gnu.warning)
-			*(.text.lock)	/* out-of-line lock text */
 			*(.rodata)
 			*(.rodata.*)
 			*(.glue_7)
diff -urN linux-2.4.18-pre2/arch/cris/Makefile linux/arch/cris/Makefile
--- linux-2.4.18-pre2/arch/cris/Makefile	Mon Oct  8 11:43:54 2001
+++ linux/arch/cris/Makefile	Thu Jan 10 13:28:32 2002
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.22 2001/10/01 14:42:38 bjornw Exp $
+# $Id: Makefile,v 1.26 2001/11/16 17:42:17 pkj Exp $
 # cris/Makefile
 #
 # This file is included by the global makefile so that you can add your own
@@ -37,10 +37,9 @@
 
 OBJCOPY := $(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S
 
-# normally, gcc on a linux box adds __linux__ but we do it "manually"
-# -mlinux enables -march=v10, -fno-underscores among others
+# -mlinux enables -march=v10, -fno-underscores, -D__linux__ among others
 
-CFLAGS := $(CFLAGS) -mlinux -fno-strict-aliasing -pipe -D__linux__
+CFLAGS := $(CFLAGS) -mlinux -pipe
 
 ifdef CONFIG_ETRAX_KGDB
 CFLAGS := $(subst -fomit-frame-pointer,,$(CFLAGS)) -g
diff -urN linux-2.4.18-pre2/arch/cris/boot/rescue/head.S linux/arch/cris/boot/rescue/head.S
--- linux-2.4.18-pre2/arch/cris/boot/rescue/head.S	Mon Oct  8 11:43:54 2001
+++ linux/arch/cris/boot/rescue/head.S	Thu Jan 10 13:28:32 2002
@@ -1,4 +1,4 @@
-/* $Id: head.S,v 1.8 2001/10/03 17:15:15 bjornw Exp $
+/* $Id: head.S,v 1.10 2001/11/08 15:10:16 starvik Exp $
  * 
  * Rescue code, made to reside at the beginning of the
  * flash-memory. when it starts, it checks a partition
@@ -114,7 +114,7 @@
 
 #define NOP_DI 0xf025050f
 #define RAM_INIT_MAGIC 0x56902387
-		
+
 	.text
 	
 	;; This is the entry point of the rescue code
@@ -144,7 +144,13 @@
 jumptarget:	
 	.dword	0xffffffff	; can be overwritten later to insert new code
 	
-no_newjump:	
+no_newjump:
+#ifdef CONFIG_ETRAX_ETHERNET		
+	;; Start MII clock to make sure it is running when tranceiver is reset
+	move.d 0x3, $r0    ; enable = on, phy = mii_clk
+	move.d $r0, [R_NETWORK_GEN_CONFIG]
+#endif
+	
 	;; We need to setup the bus registers before we start using the DRAM
 #include "../../lib/dram_init.S"
 
diff -urN linux-2.4.18-pre2/arch/cris/config.in linux/arch/cris/config.in
--- linux-2.4.18-pre2/arch/cris/config.in	Mon Oct 15 13:42:14 2001
+++ linux/arch/cris/config.in	Thu Jan 10 13:28:32 2002
@@ -35,6 +35,9 @@
 bool 'Use kernel gdb debugger' CONFIG_ETRAX_KGDB
 
 bool 'Enable Etrax100 watchdog' CONFIG_ETRAX_WATCHDOG
+if [ "$CONFIG_ETRAX_WATCHDOG" = "y" ]; then
+	bool 'Disable watchdog during Oops printouts' CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
+fi
 
 endmenu
 
diff -urN linux-2.4.18-pre2/arch/cris/cris.ld linux/arch/cris/cris.ld
--- linux-2.4.18-pre2/arch/cris/cris.ld	Mon Oct  8 11:43:54 2001
+++ linux/arch/cris/cris.ld	Thu Jan 10 13:28:32 2002
@@ -24,7 +24,6 @@
 		*(.fixup)
 		*(.text.__*)
 	}
-  	.text.lock : { *(.text.lock) }        /* out-of-line lock text */
 
 	_etext = . ;                  /* End of text section */ 
 	__etext = .;
diff -urN linux-2.4.18-pre2/arch/cris/drivers/axisflashmap.c linux/arch/cris/drivers/axisflashmap.c
--- linux-2.4.18-pre2/arch/cris/drivers/axisflashmap.c	Fri Nov  9 13:58:02 2001
+++ linux/arch/cris/drivers/axisflashmap.c	Thu Jan 10 13:28:32 2002
@@ -11,6 +11,14 @@
  * partition split defined below.
  *
  * $Log: axisflashmap.c,v $
+ * Revision 1.17  2001/11/12 19:42:38  pkj
+ * Fixed compiler warnings.
+ *
+ * Revision 1.16  2001/11/08 11:18:58  jonashg
+ * Always read from uncached address to avoid problems with flushing
+ * cachelines after write and MTD-erase. No performance loss have been
+ * seen yet.
+ *
  * Revision 1.15  2001/10/19 12:41:04  jonashg
  * Name of probe has changed in MTD.
  *
@@ -121,7 +129,7 @@
 static void flash_copy_from(struct map_info *map, void *to,
 			    unsigned long from, ssize_t len)
 {
-	memcpy(to, (void *)(FLASH_CACHED_ADDR + from), len);
+	memcpy(to, (void *)(FLASH_UNCACHED_ADDR + from), len);
 }
 
 static void flash_write8(struct map_info *map, __u8 d, unsigned long adr)
@@ -237,7 +245,7 @@
 	int use_default_ptable = 1; /* Until proven otherwise */
 	const char *pmsg = "  /dev/flash%d at 0x%x, size 0x%x\n";
 
-	printk(KERN_NOTICE "Axis flash mapping: %x at %x\n",
+	printk(KERN_NOTICE "Axis flash mapping: %x at %lx\n",
 	       WINDOW_SIZE, FLASH_CACHED_ADDR);
 
 #ifdef CONFIG_MTD_CFI
diff -urN linux-2.4.18-pre2/arch/cris/drivers/ethernet.c linux/arch/cris/drivers/ethernet.c
--- linux-2.4.18-pre2/arch/cris/drivers/ethernet.c	Mon Oct  8 11:43:54 2001
+++ linux/arch/cris/drivers/ethernet.c	Thu Jan 10 13:28:32 2002
@@ -1,4 +1,4 @@
-/* $Id: ethernet.c,v 1.18 2001/10/03 14:40:43 jonashg Exp $
+/* $Id: ethernet.c,v 1.21 2001/11/23 11:54:49 starvik Exp $
  *
  * e100net.c: A network driver for the ETRAX 100LX network controller.
  *
@@ -7,6 +7,21 @@
  * The outline of this driver comes from skeleton.c.
  *
  * $Log: ethernet.c,v $
+ * Revision 1.21  2001/11/23 11:54:49  starvik
+ * Added IFF_PROMISC and IFF_ALLMULTI handling in set_multicast_list
+ * Removed compiler warnings
+ *
+ * Revision 1.20  2001/11/12 19:26:00  pkj
+ * * Corrected e100_negotiate() to not assign half to current_duplex when
+ *   it was supposed to compare them...
+ * * Cleaned up failure handling in e100_open().
+ * * Fixed compiler warnings.
+ *
+ * Revision 1.19  2001/11/09 07:43:09  starvik
+ * Added full duplex support
+ * Added ioctl to set speed and duplex
+ * Clear LED timer only runs when LED is lit
+ *
  * Revision 1.18  2001/10/03 14:40:43  jonashg
  * Update rx_bytes counter.
  *
@@ -104,6 +119,7 @@
 #include <asm/dma.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
+#include <asm/ethernet.h>
 
 //#define ETHDEBUG
 #define D(x)
@@ -120,7 +136,7 @@
 
 static struct sockaddr default_mac = {
 	0,
-        { 0x00, 0x40, 0x8C, 0xCD, 0x00, 0x00 }
+	{ 0x00, 0x40, 0x8C, 0xCD, 0x00, 0x00 }
 };
 
 /* Information that need to be kept for each board. */
@@ -136,6 +152,14 @@
 };
 
 
+/* Duplex settings */
+enum duplex
+{
+	half,
+	full,
+	autoneg
+};
+
 /* Dma descriptors etc. */
 
 #define RX_BUF_SIZE 32768
@@ -148,9 +172,18 @@
 /* 
 ** MDIO constants.
 */
-#define MDIO_BASE_STATUS_REG   0x1
-#define MDIO_BASE_CONTROL_REG  0x0
-#define MDIO_LINK_UP_MASK      0x4
+#define MDIO_BASE_STATUS_REG                0x1
+#define MDIO_BASE_CONTROL_REG               0x0
+#define MDIO_BC_NEGOTIATE                0x0200
+#define MDIO_BC_FULL_DUPLEX_MASK         0x0100
+#define MDIO_BC_AUTO_NEG_MASK            0x1000
+#define MDIO_BC_SPEED_SELECT_MASK        0x2000
+#define MDIO_ADVERTISMENT_REG               0x4
+#define MDIO_ADVERT_100_FD                0x100
+#define MDIO_ADVERT_100_HD                0x080
+#define MDIO_ADVERT_10_FD                 0x040
+#define MDIO_ADVERT_10_HD                 0x020
+#define MDIO_LINK_UP_MASK                   0x4
 #define MDIO_START                          0x1
 #define MDIO_READ                           0x2
 #define MDIO_WRITE                          0x1
@@ -158,6 +191,7 @@
 
 /* Broadcom specific */
 #define MDIO_AUX_CTRL_STATUS_REG           0x18
+#define MDIO_FULL_DUPLEX_IND                0x1
 #define MDIO_SPEED                          0x2
 #define MDIO_PHYS_ADDR                      0x0
 
@@ -165,18 +199,25 @@
 #define NET_FLASH_TIME                  (HZ/50) /* 20 ms */
 #define NET_FLASH_PAUSE                (HZ/100) /* 10 ms */
 #define NET_LINK_UP_CHECK_INTERVAL       (2*HZ) /* 2 s   */
+#define NET_DUPLEX_CHECK_INTERVAL        (2*HZ) /* 2 s   */
 
 #define NO_NETWORK_ACTIVITY 0
 #define NETWORK_ACTIVITY    1
 
 #define RX_DESC_BUF_SIZE   256
 #define NBR_OF_RX_DESC     (RX_BUF_SIZE / \
-			    RX_DESC_BUF_SIZE)
+                            RX_DESC_BUF_SIZE)
 
 #define GET_BIT(bit,val)   (((val) >> (bit)) & 0x01)
 
+/* Define some macros to access ETRAX 100 registers */
+#define SETF(var, reg, field, val) var = (var & ~IO_MASK(##reg##, field)) | \
+					  IO_FIELD(##reg##, field, val)
+#define SETS(var, reg, field, val) var = (var & ~IO_MASK(##reg##, field)) | \
+					  IO_STATE(##reg##, field, val)
+
 static etrax_dma_descr *myNextRxDesc;  /* Points to the next descriptor to
-					  to be processed */
+                                          to be processed */
 static etrax_dma_descr *myLastRxDesc;  /* The last processed descriptor */
 static etrax_dma_descr *myPrevRxDesc;  /* The descriptor right before myNextRxDesc */
 
@@ -187,13 +228,21 @@
 
 static struct sk_buff *tx_skb;
 
+static unsigned int network_rec_config_shadow = 0;
+
 /* Network speed indication. */
 static struct timer_list speed_timer;
 static struct timer_list clear_led_timer;
-static int current_speed;
+static int current_speed; /* Speed read from tranceiver */
+static int current_speed_selection; /* Speed selected by user */
 static int led_next_time;
 static int led_active;
 
+/* Duplex */
+static struct timer_list duplex_timer;
+static int full_duplex;
+static enum duplex current_duplex;
+
 /* Index to functions, as function prototypes. */
 
 static int etrax_ethernet_init(struct net_device *dev);
@@ -206,6 +255,8 @@
 static void e100nw_interrupt(int irq, void *dev_id, struct pt_regs *regs);
 static void e100_rx(struct net_device *dev);
 static int e100_close(struct net_device *dev);
+static int e100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+static void e100_tx_timeout(struct net_device *dev);
 static struct net_device_stats *e100_get_stats(struct net_device *dev);
 static void set_multicast_list(struct net_device *dev);
 static void e100_hardware_send_packet(char *buf, int length);
@@ -213,6 +264,11 @@
 static void update_tx_stats(struct net_device_stats *);
 
 static void e100_check_speed(unsigned long dummy);
+static void e100_set_speed(unsigned long speed);
+static void e100_check_duplex(unsigned long dummy);
+static void e100_set_duplex(enum duplex);
+static void e100_negotiate(void);
+
 static unsigned short e100_get_mdio_reg(unsigned char reg_num);
 static void e100_send_mdio_cmd(unsigned short cmd, int write_cmd);
 static void e100_send_mdio_bit(unsigned char bit);
@@ -278,6 +334,8 @@
 	dev->get_stats          = e100_get_stats;
 	dev->set_multicast_list = set_multicast_list;
 	dev->set_mac_address    = e100_set_mac_address;
+	dev->do_ioctl           = e100_ioctl;
+	dev->tx_timeout         = e100_tx_timeout;
 
 	/* set the default MAC address */
 
@@ -287,7 +345,7 @@
 
 	/* Initialise receive descriptors */
 
-	for(i = 0; i < (NBR_OF_RX_DESC - 1); i++) {
+	for (i = 0; i < (NBR_OF_RX_DESC - 1); i++) {
 		RxDescList[i].ctrl   = 0;
 		RxDescList[i].sw_len = RX_DESC_BUF_SIZE;
 		RxDescList[i].next   = virt_to_phys(&RxDescList[i + 1]);
@@ -313,12 +371,18 @@
 	/* Initialize speed indicator stuff. */
 
 	current_speed = 10;
+	current_speed_selection = 0; /* Auto */
 	speed_timer.expires = jiffies + NET_LINK_UP_CHECK_INTERVAL;
 	speed_timer.function = e100_check_speed;
 	add_timer(&speed_timer);
+        
 	clear_led_timer.function = e100_clear_network_leds;
-	clear_led_timer.expires = jiffies + HZ/10;
-	add_timer(&clear_led_timer);
+        
+	full_duplex = 0;
+	current_duplex = autoneg;
+	duplex_timer.expires = jiffies + NET_DUPLEX_CHECK_INTERVAL;		
+	duplex_timer.function = e100_check_duplex;
+	add_timer(&duplex_timer);
 
 	return 0;
 }
@@ -335,7 +399,7 @@
 
 	/* remember it */
 
-        memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+	memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
 
 	/* Write it to the hardware.
 	 * Note the way the address is wrapped:
@@ -409,21 +473,21 @@
 
 	if (request_irq(NETWORK_DMA_RX_IRQ_NBR, e100rx_interrupt, 0,
 			cardname, (void *)dev)) {
-		goto grace_exit;
+		goto grace_exit0;
 	}
 
 	/* allocate the irq corresponding to the transmitting DMA */
 
 	if (request_irq(NETWORK_DMA_TX_IRQ_NBR, e100tx_interrupt, 0,
 			cardname, (void *)dev)) {
-		goto grace_exit;
+		goto grace_exit1;
 	}
 
 	/* allocate the irq corresponding to the network errors etc */
 
 	if (request_irq(NETWORK_STATUS_IRQ_NBR, e100nw_interrupt, 0,
 			cardname, (void *)dev)) {
-		goto grace_exit;
+		goto grace_exit2;
 	}
 
 	/*
@@ -431,18 +495,12 @@
 	 * and clean up on failure.
 	 */
 
-	if(request_dma(NETWORK_TX_DMA_NBR, cardname)) {
-		goto grace_exit;
+	if (request_dma(NETWORK_TX_DMA_NBR, cardname)) {
+		goto grace_exit3;
 	}
 
-	if(request_dma(NETWORK_RX_DMA_NBR, cardname)) {
-	grace_exit:
-		/* this will cause some 'trying to free free irq' but what the heck... */
-		free_dma(NETWORK_TX_DMA_NBR);
-		free_irq(NETWORK_DMA_RX_IRQ_NBR, (void *)dev);
-		free_irq(NETWORK_DMA_TX_IRQ_NBR, (void *)dev);
-		free_irq(NETWORK_STATUS_IRQ_NBR, (void *)dev);
-		return -EAGAIN;
+	if (request_dma(NETWORK_RX_DMA_NBR, cardname)) {
+		goto grace_exit4;
 	}
 
 	/* give the HW an idea of what MAC address we want */
@@ -459,9 +517,10 @@
 
 	*R_NETWORK_REC_CONFIG = 0xd; /* broadcast rec, individ. rec, ma0 enabled */
 #else
-	*R_NETWORK_REC_CONFIG =
-		IO_STATE(R_NETWORK_REC_CONFIG, broadcast, receive) |
-		IO_STATE(R_NETWORK_REC_CONFIG, ma0,       enable);
+	SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, broadcast, receive);
+	SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, ma0, enable);
+	SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex);
+	*R_NETWORK_REC_CONFIG = network_rec_config_shadow;
 #endif
 
 	*R_NETWORK_GEN_CONFIG =
@@ -507,6 +566,17 @@
 	netif_start_queue(dev);
 
 	return 0;
+
+grace_exit4:
+	free_dma(NETWORK_TX_DMA_NBR);
+grace_exit3:
+	free_irq(NETWORK_STATUS_IRQ_NBR, (void *)dev);
+grace_exit2:
+	free_irq(NETWORK_DMA_TX_IRQ_NBR, (void *)dev);
+grace_exit1:
+	free_irq(NETWORK_DMA_RX_IRQ_NBR, (void *)dev);
+grace_exit0:
+	return -EAGAIN;
 }
 
 
@@ -532,10 +602,119 @@
 	add_timer(&speed_timer);
 }
 
+static void
+e100_negotiate(void)
+{
+	unsigned short cmd;
+	unsigned short data = e100_get_mdio_reg(MDIO_ADVERTISMENT_REG);
+	int bitCounter;
+
+	/* Discard old speed and duplex settings */
+	data &= ~(MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD | 
+	          MDIO_ADVERT_10_FD | MDIO_ADVERT_10_HD);
+  
+	switch (current_speed_selection) {
+		case 10 :
+			if (current_duplex == full)
+				data |= MDIO_ADVERT_10_FD;
+			else if (current_duplex == half)
+				data |= MDIO_ADVERT_10_HD;
+			else
+				data |= MDIO_ADVERT_10_HD |  MDIO_ADVERT_10_FD;
+			break;
+
+		case 100 :
+			 if (current_duplex == full)
+				data |= MDIO_ADVERT_100_FD;
+			else if (current_duplex == half)
+				data |= MDIO_ADVERT_100_HD;
+			else
+				data |= MDIO_ADVERT_100_HD |  MDIO_ADVERT_100_FD;
+			break;
+
+		case 0 : /* Auto */
+			 if (current_duplex == full)
+				data |= MDIO_ADVERT_100_FD | MDIO_ADVERT_10_FD;
+			else if (current_duplex == half)
+				data |= MDIO_ADVERT_100_HD | MDIO_ADVERT_10_HD;
+			else
+				data |= MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD | MDIO_ADVERT_10_FD | MDIO_ADVERT_10_HD;
+			break;
+
+		default : /* assume autoneg speed and duplex */
+			data |= MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD | 
+			        MDIO_ADVERT_10_FD | MDIO_ADVERT_10_HD;
+	}
+
+	cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (MDIO_PHYS_ADDR << 7) |
+	      (MDIO_ADVERTISMENT_REG<< 2);
+
+	e100_send_mdio_cmd(cmd, 1);
+
+	/* Data... */
+	for (bitCounter=15; bitCounter>=0 ; bitCounter--) {
+		e100_send_mdio_bit(GET_BIT(bitCounter, data));
+	}
+
+	/* Renegotiate with link partner */
+	data = e100_get_mdio_reg(MDIO_BASE_CONTROL_REG);
+	data |= MDIO_BC_NEGOTIATE;
+
+	cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (MDIO_PHYS_ADDR << 7) |
+	      (MDIO_BASE_CONTROL_REG<< 2);
+
+	e100_send_mdio_cmd(cmd, 1);
+
+	/* Data... */
+	for (bitCounter=15; bitCounter>=0 ; bitCounter--) {
+		e100_send_mdio_bit(GET_BIT(bitCounter, data));
+	}  
+}
+
+static void
+e100_set_speed(unsigned long speed)
+{
+	current_speed_selection = speed;
+	e100_negotiate();
+}
+
+static void
+e100_check_duplex(unsigned long dummy)
+{
+	unsigned long data;
+
+	data = e100_get_mdio_reg(MDIO_AUX_CTRL_STATUS_REG);
+        
+	if (data & MDIO_FULL_DUPLEX_IND) {
+		if (!full_duplex) { /* Duplex changed to full? */
+			full_duplex = 1;
+			SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex);
+			*R_NETWORK_REC_CONFIG = network_rec_config_shadow;
+		}
+	} else { /* half */
+		if (full_duplex) { /* Duplex changed to half? */
+			full_duplex = 0;
+			SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex);
+			*R_NETWORK_REC_CONFIG = network_rec_config_shadow;
+		}
+	}
+
+	/* Reinitialize the timer. */
+	duplex_timer.expires = jiffies + NET_DUPLEX_CHECK_INTERVAL;
+	add_timer(&duplex_timer);
+}
+
+static void 
+e100_set_duplex(enum duplex new_duplex)
+{
+	current_duplex = new_duplex;
+	e100_negotiate();
+}
+
+
 static unsigned short
 e100_get_mdio_reg(unsigned char reg_num)
 {
-	unsigned long flags;
 	unsigned short cmd;    /* Data to be sent on MDIO port */
 	unsigned short data;   /* Data read from MDIO */
 	int bitCounter;
@@ -549,7 +728,7 @@
 	data = 0;
 	
 	/* Data... */
-	for(bitCounter=15; bitCounter>=0 ; bitCounter--) {
+	for (bitCounter=15; bitCounter>=0 ; bitCounter--) {
 		data |= (e100_receive_mdio_bit() << bitCounter);
 	}
 
@@ -563,14 +742,14 @@
 	unsigned char data = 0x2;
 	
 	/* Preamble */
-	for(bitCounter = 31; bitCounter>= 0; bitCounter--)
+	for (bitCounter = 31; bitCounter>= 0; bitCounter--)
 		e100_send_mdio_bit(GET_BIT(bitCounter, MDIO_PREAMBLE));
 
-	for(bitCounter = 15; bitCounter >= 2; bitCounter--)
+	for (bitCounter = 15; bitCounter >= 2; bitCounter--)
 		e100_send_mdio_bit(GET_BIT(bitCounter, cmd));
 
 	/* Turnaround */
-	for(bitCounter = 1; bitCounter >= 0 ; bitCounter--)
+	for (bitCounter = 1; bitCounter >= 0 ; bitCounter--)
 		if (write_cmd)
 			e100_send_mdio_bit(GET_BIT(bitCounter, data));
 		else
@@ -606,7 +785,6 @@
 static void 
 e100_reset_tranceiver(void)
 {
-	unsigned long flags;
 	unsigned short cmd;
 	unsigned short data;
 	int bitCounter;
@@ -619,7 +797,7 @@
 	
 	data |= 0x8000;
 	
-	for(bitCounter = 15; bitCounter >= 0 ; bitCounter--) {
+	for (bitCounter = 15; bitCounter >= 0 ; bitCounter--) {
 		e100_send_mdio_bit(GET_BIT(bitCounter, data));
 	}
 }
@@ -706,15 +884,14 @@
 	struct net_device *dev = (struct net_device *)dev_id;
 	unsigned long irqbits = *R_IRQ_MASK2_RD;
  
-	if(irqbits & IO_STATE(R_IRQ_MASK2_RD, dma1_eop, active)) {
-
+	if (irqbits & IO_STATE(R_IRQ_MASK2_RD, dma1_eop, active)) {
 		/* acknowledge the eop interrupt */
 
 		*R_DMA_CH1_CLR_INTR = IO_STATE(R_DMA_CH1_CLR_INTR, clr_eop, do);
 
 		/* check if one or more complete packets were indeed received */
 
-		while(*R_DMA_CH1_FIRST != virt_to_phys(myNextRxDesc)) {
+		while (*R_DMA_CH1_FIRST != virt_to_phys(myNextRxDesc)) {
 			/* Take out the buffer and give it to the OS, then
 			 * allocate a new buffer to put a packet in.
 			 */
@@ -747,8 +924,7 @@
 	struct net_local *np = (struct net_local *)dev->priv;
 
 	/* check for a dma0_eop interrupt */
-	if(irqbits & IO_STATE(R_IRQ_MASK2_RD, dma0_eop, active)) { 
-		
+	if (irqbits & IO_STATE(R_IRQ_MASK2_RD, dma0_eop, active)) { 
 		/* This protects us from concurrent execution of
 		 * our dev->hard_start_xmit function above.
 		 */
@@ -759,7 +935,7 @@
 
 		*R_DMA_CH0_CLR_INTR = IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do);
 
-		if(*R_DMA_CH0_FIRST == 0 && tx_skb) {
+		if (*R_DMA_CH0_FIRST == 0 && tx_skb) {
 			np->stats.tx_bytes += tx_skb->len;
 			np->stats.tx_packets++;
 			/* dma is ready with the transmission of the data in tx_skb, so now
@@ -784,19 +960,19 @@
 	unsigned long irqbits = *R_IRQ_MASK0_RD;
 
 	/* check for underrun irq */
-	if(irqbits & IO_STATE(R_IRQ_MASK0_RD, underrun, active)) { 
+	if (irqbits & IO_STATE(R_IRQ_MASK0_RD, underrun, active)) { 
 		*R_NETWORK_TR_CTRL = IO_STATE(R_NETWORK_TR_CTRL, clr_error, clr);
 		np->stats.tx_errors++;
 		D(printk("ethernet receiver underrun!\n"));
 	}
 
 	/* check for overrun irq */
-	if(irqbits & IO_STATE(R_IRQ_MASK0_RD, overrun, active)) { 
+	if (irqbits & IO_STATE(R_IRQ_MASK0_RD, overrun, active)) { 
 		update_rx_stats(&np->stats); /* this will ack the irq */
 		D(printk("ethernet receiver overrun!\n"));
 	}
 	/* check for excessive collision irq */
-	if(irqbits & IO_STATE(R_IRQ_MASK0_RD, excessive_col, active)) { 
+	if (irqbits & IO_STATE(R_IRQ_MASK0_RD, excessive_col, active)) { 
 		*R_NETWORK_TR_CTRL = IO_STATE(R_NETWORK_TR_CTRL, clr_error, clr);
 		np->stats.tx_errors++;
 		D(printk("ethernet excessive collisions!\n"));
@@ -809,11 +985,13 @@
 e100_rx(struct net_device *dev)
 {
 	struct sk_buff *skb;
-	int length=0;
-	int i;
+	int length = 0;
 	struct net_local *np = (struct net_local *)dev->priv;
 	struct etrax_dma_descr *mySaveRxDesc = myNextRxDesc;
 	unsigned char *skb_data_ptr;
+#ifdef ETHDEBUG
+	int i;
+#endif
 
 	if (!led_active && jiffies > led_next_time) {
 		/* light the network leds depending on the current speed. */
@@ -822,6 +1000,7 @@
 		/* Set the earliest time we may clear the LED */
 		led_next_time = jiffies + NET_FLASH_TIME;
 		led_active = 1;
+		mod_timer(&clear_led_timer, jiffies + HZ/10);
 	}
 
 	/* If the packet is broken down in many small packages then merge
@@ -842,7 +1021,7 @@
 	printk("Got a packet of length %d:\n", length);
 	/* dump the first bytes in the packet */
 	skb_data_ptr = (unsigned char *)phys_to_virt(mySaveRxDesc->buf);
-	for(i = 0; i < 8; i++) {
+	for (i = 0; i < 8; i++) {
 		printk("%d: %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x\n", i * 8,
 		       skb_data_ptr[0],skb_data_ptr[1],skb_data_ptr[2],skb_data_ptr[3],
 		       skb_data_ptr[4],skb_data_ptr[5],skb_data_ptr[6],skb_data_ptr[7]);
@@ -869,7 +1048,7 @@
 
 	/* this loop can be made using max two memcpy's if optimized */
 
-	while(mySaveRxDesc != myNextRxDesc) {
+	while (mySaveRxDesc != myNextRxDesc) {
 		memcpy(skb_data_ptr, phys_to_virt(mySaveRxDesc->buf),
 		       mySaveRxDesc->sw_len);
 		skb_data_ptr += mySaveRxDesc->sw_len;
@@ -946,6 +1125,37 @@
 	return 0;
 }
 
+static int
+e100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	/* Maybe default should return -EINVAL instead? */
+	switch (cmd) {
+		case SET_ETH_SPEED_10:                  /* 10 Mbps */
+			e100_set_speed(10);
+			break;
+		case SET_ETH_SPEED_100:                /* 100 Mbps */
+			e100_set_speed(100);
+			break;
+		case SET_ETH_SPEED_AUTO:              /* Auto negotiate speed */
+			e100_set_speed(0);
+			break;
+		case SET_ETH_DUPLEX_HALF:              /* Hhalf duplex. */
+			e100_set_duplex(half);
+			break;
+		case SET_ETH_DUPLEX_FULL:              /* Full duplex. */
+			e100_set_duplex(full);
+			break;
+		case SET_ETH_DUPLEX_AUTO:             /* Autonegotiate duplex*/
+			e100_set_duplex(autoneg);
+			break;
+		default: /* Auto neg */
+			e100_set_speed(0);
+			e100_set_duplex(autoneg);
+			break;
+	}
+	return 0;
+}
+
 static void
 update_rx_stats(struct net_device_stats *es)
 {
@@ -996,26 +1206,31 @@
 	int num_addr = dev->mc_count;
 	unsigned long int lo_bits;
 	unsigned long int hi_bits;
-	if (num_addr == -1)
+	if (dev->flags & IFF_PROMISC)
 	{
 		/* promiscuous mode */
 		lo_bits = 0xfffffffful;
 		hi_bits = 0xfffffffful;
 
-                /* Enable individual receive */
-		*R_NETWORK_REC_CONFIG =
-		  IO_STATE(R_NETWORK_REC_CONFIG, broadcast,  receive) |
-		  IO_STATE(R_NETWORK_REC_CONFIG, ma0,        enable) |
-		  IO_STATE(R_NETWORK_REC_CONFIG, individual, receive);
+		/* Enable individual receive */
+		SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, receive);
+		*R_NETWORK_REC_CONFIG = network_rec_config_shadow;
+	} else if (dev->flags & IFF_ALLMULTI) {
+		/* enable all multicasts */
+		lo_bits = 0xfffffffful;
+		hi_bits = 0xfffffffful;
+
+		/* Disable individual receive */
+		SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, discard);
+		*R_NETWORK_REC_CONFIG =  network_rec_config_shadow;
 	} else if (num_addr == 0) {
 		/* Normal, clear the mc list */
 		lo_bits = 0x00000000ul;
 		hi_bits = 0x00000000ul;
 
-                /* Disable individual receive */
-		*R_NETWORK_REC_CONFIG =
-		  IO_STATE(R_NETWORK_REC_CONFIG, broadcast,  receive) |
-		  IO_STATE(R_NETWORK_REC_CONFIG, ma0,        enable);
+		/* Disable individual receive */
+		SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, discard);
+		*R_NETWORK_REC_CONFIG =  network_rec_config_shadow;
 	} else {
 		/* MC mode, receive normal and MC packets */
 		char hash_ix;
@@ -1057,10 +1272,9 @@
 			}
 			dmi = dmi->next;
 		}
-                /* Disable individual receive */
-		*R_NETWORK_REC_CONFIG =
-		  IO_STATE(R_NETWORK_REC_CONFIG, broadcast,  receive) |
-		  IO_STATE(R_NETWORK_REC_CONFIG, ma0,        enable);
+		/* Disable individual receive */
+		SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, discard);
+		*R_NETWORK_REC_CONFIG = network_rec_config_shadow;
 	}
 	*R_NETWORK_GA_0 = lo_bits;
 	*R_NETWORK_GA_1 = hi_bits;
@@ -1078,6 +1292,7 @@
 		/* Set the earliest time we may clear the LED */
 		led_next_time = jiffies + NET_FLASH_TIME;
 		led_active = 1;
+		mod_timer(&clear_led_timer, jiffies + HZ/10);
 	}
 
 	/* configure the tx dma descriptor */
@@ -1095,16 +1310,13 @@
 static void
 e100_clear_network_leds(unsigned long dummy)
 {
-        if (led_active && jiffies > led_next_time) {
+	if (led_active && jiffies > led_next_time) {
 		e100_set_network_leds(NO_NETWORK_ACTIVITY);
 
 		/* Set the earliest time we may set the LED */
 		led_next_time = jiffies + NET_FLASH_PAUSE;
 		led_active = 0;
 	}
-
-        clear_led_timer.expires = jiffies + HZ/10;
-	add_timer(&clear_led_timer);
 }
 
 static void
@@ -1143,7 +1355,7 @@
 
 	d->init = etrax_ethernet_init;
 
-	if(register_netdev(d) == 0)
+	if (register_netdev(d) == 0)
 		return 0;
 	else
 		return -ENODEV;
diff -urN linux-2.4.18-pre2/arch/cris/drivers/gpio.c linux/arch/cris/drivers/gpio.c
--- linux-2.4.18-pre2/arch/cris/drivers/gpio.c	Fri Nov  9 13:58:02 2001
+++ linux/arch/cris/drivers/gpio.c	Thu Jan 10 13:28:32 2002
@@ -1,4 +1,4 @@
-/* $Id: gpio.c,v 1.11 2001/10/30 14:39:12 johana Exp $
+/* $Id: gpio.c,v 1.12 2001/11/12 19:42:15 pkj Exp $
  *
  * Etrax general port I/O device
  *
@@ -9,6 +9,10 @@
  *             Johan Adolfsson  (read/set directions, write)
  *
  * $Log: gpio.c,v $
+ * Revision 1.12  2001/11/12 19:42:15  pkj
+ * * Corrected return values from gpio_leds_ioctl().
+ * * Fixed compiler warnings.
+ *
  * Revision 1.11  2001/10/30 14:39:12  johana
  * Added D() around gpio_write printk.
  *
@@ -74,7 +78,9 @@
 
 static char gpio_name[] = "etrax gpio";
 
+#if 0
 static wait_queue_head_t *gpio_wq;
+#endif
 
 static int gpio_ioctl(struct inode *inode, struct file *file,
 		      unsigned int cmd, unsigned long arg);
@@ -143,7 +149,7 @@
 {
 	/* TODO poll on alarms! */
 #if 0
-        if(!ANYTHING_WANTED) {
+        if (!ANYTHING_WANTED) {
 		D(printk("gpio_select sleeping task\n"));
 	        select_wait(&gpio_wq, table);
 	        return 0;
@@ -160,16 +166,14 @@
 	unsigned char data, clk_mask, data_mask, write_msb;
 	unsigned long flags;
 	ssize_t retval = count;
-	if (verify_area(VERIFY_READ, buf, count))
-	{
+	if (verify_area(VERIFY_READ, buf, count)) {
 		return -EFAULT;
 	}
 	clk_mask = priv->clk_mask;
 	data_mask = priv->data_mask;
 	/* It must have been configured using the IO_CFG_WRITE_MODE */
 	/* Perhaps a better error code? */
-	if (clk_mask == 0 || data_mask == 0) 
-	{
+	if (clk_mask == 0 || data_mask == 0) {
 		return -EPERM;
 	}
 	write_msb = priv->write_msb;
@@ -178,7 +182,7 @@
 		int i;
 		data = *buf++;
 		if (priv->write_msb) {
-			for (i = 7; i>=0;i--) {
+			for (i = 7; i >= 0;i--) {
 				save_flags(flags); cli();
 				*priv->port = *priv->shadow &= ~clk_mask;
 				if (data & 1<<i)
@@ -190,7 +194,7 @@
 				restore_flags(flags);
 			}
 		} else {
-			for (i = 0; i<=7;i++) {
+			for (i = 0; i <= 7;i++) {
 				save_flags(flags); cli();
 				*priv->port = *priv->shadow &= ~clk_mask;
 				if (data & 1<<i)
@@ -212,13 +216,13 @@
 	struct gpio_private *priv;
 	int p = MINOR(inode->i_rdev);
 
-	if(p >= NUM_PORTS && p != LEDS)
+	if (p >= NUM_PORTS && p != LEDS)
 		return -EINVAL;
 
 	priv = (struct gpio_private *)kmalloc(sizeof(struct gpio_private), 
 					      GFP_KERNEL);
 
-	if(!priv)
+	if (!priv)
 		return -ENOMEM;
 
 	priv->minor = p;
@@ -254,10 +258,10 @@
 
 	/* unlink from alarmlist and free the private structure */
 
-	if(p == todel) {
+	if (p == todel) {
 		alarmlist = todel->next;
 	} else {
-		while(p->next != todel)
+		while (p->next != todel)
 			p = p->next;
 		p->next = todel->next;
 	}
@@ -280,7 +284,7 @@
 {
 	unsigned long flags;
 	struct gpio_private *priv = (struct gpio_private *)file->private_data;
-	if(_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) {
+	if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) {
 		return -EINVAL;
 	}
 
@@ -353,7 +357,7 @@
 			if (!((priv->clk_mask & priv->changeable_bits) &&
 			      (priv->data_mask & priv->changeable_bits) &&
 			      (priv->clk_mask & *priv->dir_shadow) &&
-			      (priv->data_mask & *priv->dir_shadow)) )
+			      (priv->data_mask & *priv->dir_shadow)))
 			{
 				priv->clk_mask = 0;
 				priv->data_mask = 0;
@@ -361,7 +365,7 @@
 			}
 			break;
 		default:
-			if(priv->minor == LEDS)
+			if (priv->minor == LEDS)
 				return gpio_leds_ioctl(cmd, arg);
                         else
 				return -EINVAL;
@@ -375,6 +379,7 @@
 {
 	unsigned char green;
 	unsigned char red;
+
 	switch (_IOC_NR(cmd)) {
 		case IO_LEDACTIVE_SET:
 			green = ((unsigned char) arg) & 1;
@@ -382,14 +387,20 @@
 			LED_ACTIVE_SET_G(green);
 			LED_ACTIVE_SET_R(red);
 			break;
-                case IO_LED_SETBIT:                 
-                        LED_BIT_SET(arg);
-                       break;
-                case IO_LED_CLRBIT:
-                        LED_BIT_CLR(arg);
+
+		case IO_LED_SETBIT:                 
+			LED_BIT_SET(arg);
+			break;
+
+		case IO_LED_CLRBIT:
+			LED_BIT_CLR(arg);
+			break;
+
 		default:
 			return -EINVAL;
 	}
+
+	return 0;
 }
 
 struct file_operations gpio_fops = {
@@ -406,30 +417,32 @@
 static __init int
 gpio_init(void)
 {
-	int res,i;
+	extern void init_ioremap(void);
+	int res;
+#if defined (CONFIG_ETRAX_CSP0_LEDS)
+	int i;
+#endif
 
 	/* do the formalities */
 
 	res = register_chrdev(GPIO_MAJOR, gpio_name, &gpio_fops);
-	if(res < 0) {
+	if (res < 0) {
 		printk(KERN_ERR "gpio: couldn't get a major number.\n");
 		return res;
 	}
 
         /* Clear all leds */
-#if defined (CONFIG_ETRAX_CSP0_LEDS) ||  defined (CONFIG_ETRAX_PA_LEDS)         || defined (CONFIG_ETRAX_PB_LEDS) 
-
-        init_ioremap();
-        LED_NETWORK_SET(0);
-        LED_ACTIVE_SET(0);
-        LED_DISK_READ(0);
-        LED_DISK_WRITE(0);        
+#if defined (CONFIG_ETRAX_CSP0_LEDS) ||  defined (CONFIG_ETRAX_PA_LEDS) || defined (CONFIG_ETRAX_PB_LEDS) 
+	init_ioremap();
+	LED_NETWORK_SET(0);
+	LED_ACTIVE_SET(0);
+	LED_DISK_READ(0);
+	LED_DISK_WRITE(0);        
 
 #if defined (CONFIG_ETRAX_CSP0_LEDS)
-        for( i = 0; i < 32; i ++)
-        {
-            LED_BIT_SET(i);
-        }
+	for (i = 0; i < 32; i++) {
+		LED_BIT_SET(i);
+	}
 #endif
 
 #endif
diff -urN linux-2.4.18-pre2/arch/cris/drivers/serial.c linux/arch/cris/drivers/serial.c
--- linux-2.4.18-pre2/arch/cris/drivers/serial.c	Fri Nov  9 13:58:02 2001
+++ linux/arch/cris/drivers/serial.c	Thu Jan 10 13:28:32 2002
@@ -1,4 +1,4 @@
-/* $Id: serial.c,v 1.23 2001/10/30 17:53:26 pkj Exp $
+/* $Id: serial.c,v 1.28 2001/12/18 15:04:53 johana Exp $
  *
  * Serial port driver for the ETRAX 100LX chip
  *
@@ -7,6 +7,34 @@
  *      Many, many authors. Based once upon a time on serial.c for 16x50.
  *
  * $Log: serial.c,v $
+ * Revision 1.28  2001/12/18 15:04:53  johana
+ * Cleaned up write_rs485() - now it works correctly without padding extra
+ * char.
+ * Added sane default initialisation of rs485.
+ * Added #ifdef around dummy variables.
+ *
+ * Revision 1.27  2001/11/29 17:00:41  pkj
+ * 2kB seems to be too small a buffer when using 921600 bps,
+ * so increase it to 4kB (this was already done for the elinux
+ * version of the serial driver).
+ *
+ * Revision 1.26  2001/11/19 14:20:41  pkj
+ * Minor changes to comments and unused code.
+ *
+ * Revision 1.25  2001/11/12 20:03:43  pkj
+ * Fixed compiler warnings.
+ *
+ * Revision 1.24  2001/11/12 15:10:05  pkj
+ * Total redesign of the receiving part of the serial driver.
+ * Uses eight chained descriptors to write to a 4kB buffer.
+ * This data is then serialised into a 2kB buffer. From there it
+ * is copied into the TTY's flip buffers when they become available.
+ * A lot of copying, and the sizes of the buffers might need to be
+ * tweaked, but all in all it should work better than the previous
+ * version, without the need to modify the TTY code in any way.
+ * Also note that erroneous bytes are now correctly marked in the
+ * flag buffers (instead of always marking the first byte).
+ *
  * Revision 1.23  2001/10/30 17:53:26  pkj
  * * Set info->uses_dma to 0 when a port is closed.
  * * Mark the timer1 interrupt as a fast one (SA_INTERRUPT).
@@ -123,12 +151,12 @@
  * Changed %ul to %lu in printf's
  *
  * Revision 1.47  2000/10/18 15:06:53  pkj
- * Compile correctly with CONFIG_ETRAX100_SERIAL_FLUSH_DMA_FAST and
- * CONFIG_SERIAL_PROC_ENTRY together.
+ * Compile correctly with CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST and
+ * CONFIG_ETRAX_SERIAL_PROC_ENTRY together.
  * Some clean-up of the /proc/serial file.
  *
  * Revision 1.46  2000/10/16 12:59:40  johana
- * Added CONFIG_SERIAL_PROC_ENTRY for statistics and debug info.
+ * Added CONFIG_ETRAX_SERIAL_PROC_ENTRY for statistics and debug info.
  *
  * Revision 1.45  2000/10/13 17:10:59  pkj
  * Do not flush DMAs while flipping TTY buffers.
@@ -171,7 +199,7 @@
  * Uncomment definition of SERIAL_HANDLE_EARLY_ERRORS.
  *
  * Revision 1.36  2000/09/20 13:12:52  johana
- * Support for CONFIG_ETRAX100_SERIAL_RX_TIMEOUT_TICKS:
+ * Support for CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS:
  *   Number of timer ticks between flush of receive fifo (1 tick = 10ms).
  *   Try 0-3 for low latency applications. Approx 5 for high load
  *   applications (e.g. PPP). Maybe this should be more adaptive some day...
@@ -255,7 +283,7 @@
  *
  */
 
-static char *serial_version = "$Revision: 1.23 $";
+static char *serial_version = "$Revision: 1.28 $";
 
 #include <linux/config.h>
 #include <linux/version.h>
@@ -272,6 +300,7 @@
 #include <linux/string.h>
 #include <linux/fcntl.h>
 #include <linux/mm.h>
+#include <linux/slab.h>
 #if (LINUX_VERSION_CODE >= 131343)
 #include <linux/init.h>
 #endif
@@ -302,6 +331,8 @@
 #include "serial_compat.h"
 #endif
 
+#define _INLINE_ inline
+
 static DECLARE_TASK_QUEUE(tq_serial);
 
 struct tty_driver serial_driver, callout_driver;
@@ -313,11 +344,6 @@
 #define SERIAL_TYPE_CALLOUT	2
 #endif
 
-#define DEBUG_LOG(line, string, value)
-
-/* Add an x here to log a lot of timer stuff */
-#define TIMERD(x)
-
 /* number of characters left in xmit buffer before we ask for more */
 #define WAKEUP_CHARS 256
 
@@ -337,6 +363,14 @@
 
 #define TTY_THROTTLE_LIMIT (TTY_FLIPBUF_SIZE/10)
 
+#define SERIAL_RECV_SIZE      4096
+#define SERIAL_DESCR_BUF_SIZE 512
+
+/* Add an x here to log a lot of timer stuff */
+#define TIMERD(x)
+
+#define DEBUG_LOG(line, string, value)
+
 #ifndef CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS
 /* Default number of timer ticks before flushing rx fifo 
  * When using "little data, low latency applications: use 0
@@ -345,8 +379,6 @@
 #define CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS 5 
 #endif
 
-#define _INLINE_ inline
-
 static void change_speed(struct e100_serial *info);
 static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
 static int rs_write(struct tty_struct * tty, int from_user,
@@ -403,9 +435,9 @@
 static struct e100_serial rs_table[] = {
 	{ DEF_BAUD, (unsigned char *)R_SERIAL0_CTRL, 1U << 12, /* uses DMA 6 and 7 */
 	  R_DMA_CH6_CLR_INTR, R_DMA_CH6_FIRST, R_DMA_CH6_CMD,
-	  R_DMA_CH6_STATUS, R_DMA_CH6_HWSW,
+	  R_DMA_CH6_STATUS, R_DMA_CH6_HWSW, R_DMA_CH6_DESCR,
 	  R_DMA_CH7_CLR_INTR, R_DMA_CH7_FIRST, R_DMA_CH7_CMD,
-	  R_DMA_CH7_STATUS, R_DMA_CH7_HWSW,
+	  R_DMA_CH7_STATUS, R_DMA_CH7_HWSW, R_DMA_CH7_DESCR,
 	  STD_FLAGS, DEF_RX, DEF_TX, 2,
 #ifdef CONFIG_ETRAX_SERIAL_PORT0
           1
@@ -416,9 +448,9 @@
 #ifndef CONFIG_SVINTO_SIM
 	{ DEF_BAUD, (unsigned char *)R_SERIAL1_CTRL, 1U << 16, /* uses DMA 8 and 9 */
 	  R_DMA_CH8_CLR_INTR, R_DMA_CH8_FIRST, R_DMA_CH8_CMD,
-	  R_DMA_CH8_STATUS, R_DMA_CH8_HWSW,
+	  R_DMA_CH8_STATUS, R_DMA_CH8_HWSW, R_DMA_CH8_DESCR,
 	  R_DMA_CH9_CLR_INTR, R_DMA_CH9_FIRST, R_DMA_CH9_CMD,
-	  R_DMA_CH9_STATUS, R_DMA_CH9_HWSW,
+	  R_DMA_CH9_STATUS, R_DMA_CH9_HWSW, R_DMA_CH9_DESCR,
 	  STD_FLAGS, DEF_RX, DEF_TX, 3 ,
 #ifdef CONFIG_ETRAX_SERIAL_PORT1
           1
@@ -429,9 +461,9 @@
 
 	{ DEF_BAUD, (unsigned char *)R_SERIAL2_CTRL, 1U << 4,  /* uses DMA 2 and 3 */
 	  R_DMA_CH2_CLR_INTR, R_DMA_CH2_FIRST, R_DMA_CH2_CMD,
-	  R_DMA_CH2_STATUS, R_DMA_CH2_HWSW,
+	  R_DMA_CH2_STATUS, R_DMA_CH2_HWSW, R_DMA_CH2_DESCR,
 	  R_DMA_CH3_CLR_INTR, R_DMA_CH3_FIRST, R_DMA_CH3_CMD,
-	  R_DMA_CH3_STATUS, R_DMA_CH3_HWSW,
+	  R_DMA_CH3_STATUS, R_DMA_CH3_HWSW, R_DMA_CH3_DESCR,
 	  STD_FLAGS, DEF_RX, DEF_TX, 0,
 #ifdef CONFIG_ETRAX_SERIAL_PORT2
           1
@@ -442,9 +474,9 @@
 
 	{ DEF_BAUD, (unsigned char *)R_SERIAL3_CTRL, 1U << 8,  /* uses DMA 4 and 5 */
 	  R_DMA_CH4_CLR_INTR, R_DMA_CH4_FIRST, R_DMA_CH4_CMD,
-	  R_DMA_CH4_STATUS, R_DMA_CH4_HWSW,
+	  R_DMA_CH4_STATUS, R_DMA_CH4_HWSW, R_DMA_CH4_DESCR,
 	  R_DMA_CH5_CLR_INTR, R_DMA_CH5_FIRST, R_DMA_CH5_CMD,
-	  R_DMA_CH5_STATUS, R_DMA_CH5_HWSW,
+	  R_DMA_CH5_STATUS, R_DMA_CH5_HWSW, R_DMA_CH5_DESCR,
 	  STD_FLAGS, DEF_RX, DEF_TX, 1,
 #ifdef CONFIG_ETRAX_SERIAL_PORT3
           1
@@ -462,9 +494,9 @@
 static struct termios *serial_termios[NR_PORTS];
 static struct termios *serial_termios_locked[NR_PORTS];
 
-#ifdef CONFIG_SERIAL_PROC_ENTRY
+#ifdef CONFIG_ETRAX_SERIAL_PROC_ENTRY
 #define PROCSTAT(x) x
-struct ser_statistics_type{
+struct ser_statistics_type {
 	int overrun_cnt;
 	int early_errors_cnt;
 	int ser_ints_ok_cnt;
@@ -484,7 +516,7 @@
 
 #define PROCSTAT(x)
 
-#endif /* CONFIG_SERIAL_PROC_ENTRY */
+#endif /* CONFIG_ETRAX_SERIAL_PROC_ENTRY */
 
 /* RS-485 */
 #if defined(CONFIG_ETRAX_RS485)
@@ -497,14 +529,20 @@
 /* For now we assume that all bits are on the same port for each serial port */
 
 /* Dummy shadow variables */
+#if !defined(CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PB)
 static unsigned char dummy_ser0 = 0x00;
-static unsigned char dummy_ser1 = 0x00;
-static unsigned char dummy_ser2 = 0x00;
-static unsigned char dummy_ser3 = 0x00;
-
 static unsigned char dummy_dir_ser0 = 0x00;
+#endif
+#if !defined(CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_ON_PB)
+static unsigned char dummy_ser1 = 0x00;
 static unsigned char dummy_dir_ser1 = 0x00;
+#endif
+#if !defined(CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_ON_PA)
+static unsigned char dummy_ser2 = 0x00;
 static unsigned char dummy_dir_ser2 = 0x00;
+#endif
+
+static unsigned char dummy_ser3 = 0x00;
 static unsigned char dummy_dir_ser3 = 0x00;
 
 /* Info needed for each ports extra control/status signals.
@@ -523,52 +561,56 @@
 
 static const struct control_pins e100_modem_pins[NR_PORTS] = 
 {
-/* Ser 0 */
-  {
+	/* Ser 0 */
+	{
 #if defined(CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PB)
-    R_PORT_PB_DATA,  &port_pb_data_shadow,  &port_pb_dir_shadow,
-    CONFIG_ETRAX_SER0_DTR_ON_PB_BIT,
-    CONFIG_ETRAX_SER0_RI_ON_PB_BIT, 
-    CONFIG_ETRAX_SER0_DSR_ON_PB_BIT, 
-    CONFIG_ETRAX_SER0_CD_ON_PB_BIT
+		R_PORT_PB_DATA, &port_pb_data_shadow, &port_pb_dir_shadow,
+		CONFIG_ETRAX_SER0_DTR_ON_PB_BIT,
+		CONFIG_ETRAX_SER0_RI_ON_PB_BIT,
+		CONFIG_ETRAX_SER0_DSR_ON_PB_BIT,
+		CONFIG_ETRAX_SER0_CD_ON_PB_BIT
 #else
-    &dummy_ser0, &dummy_ser0, &dummy_dir_ser0, 0, 1, 2, 3
-#endif   
-  },
-/* Ser 1 */
-  {
+		&dummy_ser0, &dummy_ser0, &dummy_dir_ser0, 0, 1, 2, 3
+#endif
+	},
+
+	/* Ser 1 */
+	{
 #if defined(CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_ON_PB)
-    R_PORT_PB_DATA,  &port_pb_data_shadow,  &port_pb_dir_shadow,
-    CONFIG_ETRAX_SER1_DTR_ON_PB_BIT,
-    CONFIG_ETRAX_SER1_RI_ON_PB_BIT, 
-    CONFIG_ETRAX_SER1_DSR_ON_PB_BIT, 
-    CONFIG_ETRAX_SER1_CD_ON_PB_BIT
+		R_PORT_PB_DATA, &port_pb_data_shadow, &port_pb_dir_shadow,
+		CONFIG_ETRAX_SER1_DTR_ON_PB_BIT,
+		CONFIG_ETRAX_SER1_RI_ON_PB_BIT,
+		CONFIG_ETRAX_SER1_DSR_ON_PB_BIT,
+		CONFIG_ETRAX_SER1_CD_ON_PB_BIT
 #else
-    &dummy_ser1, &dummy_ser1, &dummy_dir_ser1, 0, 1, 2, 3
-#endif   
-  },  
-/* Ser 2 */
-  {
+		&dummy_ser1, &dummy_ser1, &dummy_dir_ser1, 0, 1, 2, 3
+#endif
+	},
+
+	/* Ser 2 */
+	{
 #if defined(CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_ON_PA)
-    R_PORT_PA_DATA,  &port_pa_data_shadow,  &port_pa_dir_shadow,
-    CONFIG_ETRAX_SER2_DTR_ON_PA_BIT,
-    CONFIG_ETRAX_SER2_RI_ON_PA_BIT, 
-    CONFIG_ETRAX_SER2_DSR_ON_PA_BIT, 
-    CONFIG_ETRAX_SER2_CD_ON_PA_BIT
+		R_PORT_PA_DATA, &port_pa_data_shadow, &port_pa_dir_shadow,
+		CONFIG_ETRAX_SER2_DTR_ON_PA_BIT,
+		CONFIG_ETRAX_SER2_RI_ON_PA_BIT,
+		CONFIG_ETRAX_SER2_DSR_ON_PA_BIT,
+		CONFIG_ETRAX_SER2_CD_ON_PA_BIT
 #else
-    &dummy_ser2, &dummy_ser2, &dummy_dir_ser2, 0, 1, 2, 3
-#endif   
-  },
-/* Ser 3 */
-  {
-    &dummy_ser3, &dummy_ser3, &dummy_dir_ser3, 0, 1, 2, 3
-  }
+		&dummy_ser2, &dummy_ser2, &dummy_dir_ser2, 0, 1, 2, 3
+#endif
+	},
+
+	/* Ser 3 */
+	{
+		&dummy_ser3, &dummy_ser3, &dummy_dir_ser3, 0, 1, 2, 3
+	}
 };
 
 #if defined(CONFIG_ETRAX_RS485) && defined(CONFIG_ETRAX_RS485_ON_PA)
 unsigned char rs485_pa_port = CONFIG_ETRAX_RS485_ON_PA_BIT;
 #endif
 
+
 #define E100_RTS_MASK 0x20
 #define E100_CTS_MASK 0x40
 
@@ -840,8 +882,8 @@
 {
 #ifndef CONFIG_SVINTO_SIM
 	/* disable the receiver */
-	info->port[REG_REC_CTRL] = info->rx_ctrl &=
-		~IO_MASK(R_SERIAL0_REC_CTRL, rec_enable);
+	info->port[REG_REC_CTRL] =
+		(info->rx_ctrl &= ~IO_MASK(R_SERIAL0_REC_CTRL, rec_enable));
 #endif
 }
 
@@ -850,8 +892,8 @@
 {
 #ifndef CONFIG_SVINTO_SIM
 	/* enable the receiver */
-	info->port[REG_REC_CTRL] = info->rx_ctrl |=
-		IO_MASK(R_SERIAL0_REC_CTRL, rec_enable);
+	info->port[REG_REC_CTRL] =
+		(info->rx_ctrl |= IO_MASK(R_SERIAL0_REC_CTRL, rec_enable));
 #endif
 }
 
@@ -943,13 +985,8 @@
 static int
 e100_write_rs485(struct tty_struct *tty,struct rs485_write *r)
 {
-	int stop_delay;
-	int total, i;
-	int max_j, delay_ms, bits;
-	tcflag_t cflags;
-	int size = (*r).outc_size;
+	int total;
 	struct e100_serial * info = (struct e100_serial *)tty->driver_data;
-	struct wait_queue wait = { current, NULL };
 
 	/* If we are in RS-485 mode, we need to toggle RTS and disable
 	 * the receiver before initiating a DMA transfer
@@ -975,44 +1012,20 @@
 	 * enable the receiver
 	 */	
 
-	/* wait on transmit shift register */
-	/* All is sent, check if we should wait more before toggling rts */
-	
-	/* calc. number of bits / data byte */
-	cflags = info->tty->termios->c_cflag;
-
-	/* databits + startbit and 1 stopbit */
-	if ((cflags & CSIZE) == CS7)
-		bits = 9;
-	else
-		bits = 10;  
-
-	if (cflags & CSTOPB)     /* 2 stopbits ? */
-		bits++;
-
-	if (cflags & PARENB)     /* parity bit ? */
-		bits++;
-	
-	/* calc timeout */
-	delay_ms = ((bits * size * 1000) / info->baud) + 1;
-	max_j = jiffies + (delay_ms * HZ)/1000 + 10;
-
-	while (jiffies < max_j) {
-		if (info->port[REG_STATUS] &
-		    IO_STATE(R_SERIAL0_STATUS, tr_ready, ready)) {
-			for (i = 0; i < 100; i++)
-				;
-			if (info->port[REG_STATUS] &
-			    IO_STATE(R_SERIAL0_STATUS, tr_ready, ready)) {
-				/* ~25 for loops per usec */
-				stop_delay = 1000000 / info->baud;
-				if (cflags & CSTOPB) 
-					stop_delay *= 2;
-				udelay(stop_delay);
-				break;
-			}
-		}
+	/* Sleep until all sent */
+	tty_wait_until_sent(tty, 0);
+#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
+	/* Now sleep a little more so that shift register is empty */
+	schedule_usleep(info->char_time_usec * 2);
+#else
+	{
+		unsigned int val;
+		/* wait on transmit shift register */
+		do{
+			get_lsr_info(info, &val);
+		}while (!(val & TIOCSER_TEMT));
 	}
+#endif
 
 	e100_rts(info, info->rs485.rts_after_sent);
 	
@@ -1118,7 +1131,7 @@
 	}
 	return;
 #endif
-	/* acknowledge both a dma_descr and dma_eop irq in R_DMAx_CLRINTR */
+	/* acknowledge both dma_descr and dma_eop irq in R_DMA_CHx_CLR_INTR */
 	*info->oclrintradr =
 		IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
 		IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
@@ -1168,14 +1181,14 @@
 
 #if defined(CONFIG_ETRAX_RS485)
 		/* Check if we should toggle RTS now */
-		if (info->rs485.enabled)
-		{
+		if (info->rs485.enabled) {
 			/* Make sure fifo is empty */
-			int in_fifo = 0 ;
-			do{
+			int in_fifo = 0;
+
+			do {
 				in_fifo = IO_EXTRACT(R_DMA_CH6_STATUS, avail,
-						    *info->ostatusadr);
-			}  while (in_fifo > 0) ;
+						     *info->ostatusadr);
+			}  while (in_fifo > 0);
 			/* Any way to really check transmitter empty? (TEMT) */
 			/* Control RTS to set to RX mode */
 			e100_rts(info, info->rs485.rts_after_sent); 
@@ -1201,7 +1214,6 @@
 	*info->ocmdadr = 1;       /* dma command start -> R_DMAx_CMD */
 	
 	/* DMA is now running (hopefully) */
-
 }
 
 static void 
@@ -1220,13 +1232,122 @@
 	transmit_chars(info);
 }
 
+#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
+static int serial_fast_timer_started = 0;
+static int serial_fast_timer_expired = 0;
+static void flush_timeout_function(unsigned long data);
+#define START_FLUSH_FAST_TIMER(info, string) {\
+  unsigned long timer_flags; \
+  save_flags(timer_flags); \
+  cli(); \
+  if (fast_timers[info->line].function == NULL) { \
+    serial_fast_timer_started++; \
+    TIMERD(DEBUG_LOG(info->line, "start_timer %i ", info->line)); \
+    TIMERD(DEBUG_LOG(info->line, "num started: %i\n", serial_fast_timer_started)); \
+    start_one_shot_timer(&fast_timers[info->line], \
+                         flush_timeout_function, \
+                         (unsigned long)info, \
+                         info->char_time_usec*4, \
+                         string); \
+  } \
+  else { \
+    TIMERD(DEBUG_LOG(info->line, "timer %i already running\n", info->line)); \
+  } \
+  restore_flags(timer_flags); \
+}
+
+#else
+#define START_FLUSH_FAST_TIMER(info, string)
+#endif
+
+static int
+add_char_and_flag(struct e100_serial *info, unsigned char data, unsigned char flag)
+{
+	if (!CIRC_SPACE(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE))
+		return 0;
+
+	info->recv.buf[info->recv.head] = data;
+	info->flag_buf[info->recv.head] = flag;
+	info->recv.head = (info->recv.head + 1) & (SERIAL_RECV_SIZE - 1);
+
+	info->icount.rx++;
+
+	return 1;
+}
+
+static _INLINE_ unsigned int
+copy_descr_data(struct e100_serial *info, unsigned int recvl, unsigned char *buf)
+{
+	unsigned int count = CIRC_SPACE_TO_END(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE);
+	unsigned int length = 0;
+
+	 while (length < recvl && count) {
+		if (length + count > recvl)
+			count = recvl - length;
+
+		memcpy(info->recv.buf + info->recv.head, buf + length, count);
+		memset(info->flag_buf + info->recv.head, '\0', count);
+		info->recv.head = (info->recv.head + count) & (SERIAL_RECV_SIZE - 1);
+		length += count;
+
+		count = CIRC_SPACE_TO_END(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE);
+	}
+
+	if (length != recvl) { 
+		printk(__FUNCTION__ ": Buffer overflow! %d byte(s) did not fit.\n", recvl - length);
+		PROCSTAT(ser_stat[info->line].overrun_cnt += recvl - length);
+	}
+
+	return length;
+}
+
+static _INLINE_ unsigned int
+copy_all_descr_data(struct e100_serial *info)
+{
+	struct etrax_dma_descr *descr;
+	unsigned int recvl;
+	unsigned int ret = 0;
+
+	while (1)
+	{
+		descr = &info->rec_descr[info->cur_rec_descr];
+
+		if (descr == phys_to_virt(*info->idescradr))
+			break;
+
+		if (++info->cur_rec_descr == SERIAL_RECV_DESCRIPTORS)
+			info->cur_rec_descr = 0;
+	
+		/* find out how many bytes were read */
+
+		/* if the eop bit was not set, all data has been received */
+		if (!(descr->status & d_eop)) {
+			recvl = descr->sw_len;
+		} else {
+			/* otherwise we find the amount of data received here */
+			recvl = descr->hw_len;
+		}
+
+		/* Reset the status information */
+		descr->status = 0;
+
+		DEBUG_LOG(info->line, "recvl %lu\n", recvl);
+
+		/* update stats */
+		info->icount.rx += recvl;
+
+		ret += copy_descr_data(info, recvl, phys_to_virt(descr->buf));
+	}
+
+	return ret;
+}
+
 static _INLINE_ void 
 receive_chars(struct e100_serial *info)
 {
 	struct tty_struct *tty;
 	unsigned char rstat;
-	unsigned int recvl;
-	struct etrax_dma_descr *descr;
+	unsigned int old_head;
 
 #ifdef CONFIG_SVINTO_SIM
 	/* No receive in the simulator.  Will probably be when the rest of
@@ -1235,168 +1356,96 @@
 	return;
 #endif
 
-	tty = info->tty;
-
-	/* acknowledge both a dma_descr and dma_eop irq in R_DMAx_CLRINTR */
-
-	// ?
+	/* Acknowledge both dma_descr and dma_eop irq in R_DMA_CHx_CLR_INTR */
 	*info->iclrintradr =
 		IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
 		IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
 
-	if (!tty) /* something wrong... */
+	tty = info->tty;
+	if (!tty) /* Something wrong... */
 		return;
 
-	descr = &info->rec_descr;
+#ifdef SERIAL_HANDLE_EARLY_ERRORS
+	e100_enable_serial_data_irq(info);
+#endif	
 
-	/* find out how many bytes were read */
+	if (info->errorcode == ERRCODE_INSERT_BREAK)
+		add_char_and_flag(info, '\0', TTY_BREAK);
 
-	/* if the eop bit was not set, all data has been received */
-	if (!(descr->status & d_eop)) {
-		recvl = descr->sw_len;
-	} else {
-		/* otherwise we find the amount of data received here */
-		recvl = descr->hw_len;
-	}
+	old_head = info->recv.head;
+	
+	if (copy_all_descr_data(info) && info->errorcode == ERRCODE_SET_BREAK)
+		info->flag_buf[old_head] = TTY_BREAK;
 
-	/* read the status register so we can detect errors,
-	 * but we can't really do anything about those errors
-	 * anyway, since we have the DMA in "force eop at error" mode
-	 * the fault characters are not in the buffer anyway.
-	 */
+	info->errorcode = 0;
 
+	/* Read the status register to detect errors */
 	rstat = info->port[REG_STATUS];
 
-	if ((rstat & SER_ERROR_MASK) != 0) {
-		unsigned char data;
-		/* if we got an error, we must reset it by reading the
+	if (rstat & SER_ERROR_MASK) {
+		/* If we got an error, we must reset it by reading the
 		 * data_in field
 		 */
-		data = info->port[REG_DATA];
+		unsigned char data = info->port[REG_DATA];
+
 		PROCSTAT(ser_stat[info->line].errors_cnt++);
-		DEBUG_LOG(info->line, " #dERR: s d 0x%04X\n",
+		DEBUG_LOG(info->line, "#dERR: s d 0x%04X\n",
 			  ((rstat & SER_ERROR_MASK) << 8) | data);
-		/* Only handle the saved error code, that indicates that we got
-		 * the last character of a break that looks like it's ok, but
-		 * is not
-		 */
 
-		if (info->errorcode == 0) {
-			*tty->flip.flag_buf_ptr = TTY_NORMAL;
-		} else {
-			unsigned char data;
-			data = info->port[REG_DATA];
-			if (info->errorcode & ERRCODE_INSERT) {
-				unsigned char *currbuf;
-				/* Get the current buffer */
-				if (tty->flip.buf_num) {
-					currbuf = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
-				} else {
-					currbuf = tty->flip.char_buf;
-				}
-				/* We should insert a character in the buffer! */
-				if (recvl == 0) {
-					recvl = 1;
-					DEBUG_LOG(info->line, "insert to %lu\n", recvl);
-				} else {
-					/* Move stuff around.. */
-					DEBUG_LOG(info->line, "#insert to %lu!\n", recvl);
-					if (recvl < TTY_FLIPBUF_SIZE) {
-						int i;
-						/* Move the data 1 step right */
-						i = recvl;
-						while (i) {
-							currbuf[i] = currbuf[i-1];
-							i--;
-						}
-						recvl++;
-					} else {
-						/* We can't move it all! Skip break! */
-						/* TODO: Handle full buffer? */
-						DEBUG_LOG(info->line, "#BRK skipped! %lu!\n", recvl);
-						info->errorcode = 0;
-					}
-				}
-			}
-	    
-			PROCSTAT(ser_stat[info->line].errors_cnt++);
-			DEBUG_LOG(info->line, " #bERR: s d 0x%04X\n",
-				  ((rstat & SER_ERROR_MASK) << 8) | data);
-			*tty->flip.flag_buf_ptr = (info->errorcode & 0xFF);
-			info->errorcode = 0;
-#if 0
-			printk("SERERR: 0x%02X data: 0x%02X\n", rstat & SER_ERROR_MASK, data);
-#endif
-			/* we only ever write errors into the first byte in
-			 * the flip flag buffer, so we dont have to clear it
-			 * all every time
-			 */
-		}
+		if (rstat & SER_PAR_ERR_MASK)
+			add_char_and_flag(info, data, TTY_PARITY);
+		else if (rstat & SER_OVERRUN_MASK)
+			add_char_and_flag(info, data, TTY_OVERRUN);
+		else if (rstat & SER_FRAMING_ERR_MASK)
+			add_char_and_flag(info, data, TTY_FRAME);
 	}
 
-	DEBUG_LOG(info->line, "recvl %lu\n", recvl);
-
-	if (recvl) {
-		unsigned char *buf;
-		struct async_icount *icount = &info->icount;
-
-		/* update stats */
-		icount->rx += recvl;
-
-		/* use the flip buffer next in turn to restart DMA into */
-
-		if (tty->flip.buf_num) {
-			buf = tty->flip.char_buf;
-		} else {
-			buf = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
-		}
+	if (!E100_RTS_GET(info) &&
+	    CIRC_SPACE(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE) < TTY_THROTTLE_LIMIT)
+		info->tty->driver.throttle(info->tty);
+	
+	START_FLUSH_FAST_TIMER(info, "receive_chars");
 
-		if (buf == phys_to_virt(descr->buf)) {
-			printk("ttyS%d flip-buffer overrun!\n", info->line);
-			icount->overrun++;
-			*tty->flip.flag_buf_ptr = TTY_OVERRUN;
-			/* restart old buffer */
-		} else {
-			descr->buf = virt_to_phys(buf);
+	/* Restart the receiving DMA */
+	*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, restart);
+}
 
-			/* schedule or push a flip of the buffer */
+static _INLINE_ int
+start_recv_dma(struct e100_serial *info)
+{
+	struct etrax_dma_descr *descr = info->rec_descr;
+	unsigned char *buf = info->recv.buf + 2*SERIAL_RECV_SIZE;
+        int i;
 
-			info->tty->flip.count = recvl;
+	/* Set up the receiving descriptors */
+	for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++) {
+		descr[i].ctrl = d_int;
+		descr[i].buf = virt_to_phys(buf);
+		descr[i].sw_len = SERIAL_DESCR_BUF_SIZE;
+		descr[i].hw_len = 0;
+		descr[i].status = 0;
+		descr[i].next = virt_to_phys(&descr[i+1]);
 
-#if (LINUX_VERSION_CODE > 131394) /* 2.1.66 */
-			/* this includes a check for low-latency */
-			tty_flip_buffer_push(tty);
-#else
-			queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
-#endif	
-		}
+		buf += SERIAL_DESCR_BUF_SIZE;
 	}
 
-	/* restart the receiving dma */
+	/* Link the last descriptor to the first */
+	descr[i-1].next = virt_to_phys(&descr[0]);
 
-	descr->sw_len = TTY_FLIPBUF_SIZE;
-	descr->ctrl = d_int | d_eol | d_eop;
-	descr->hw_len = 0;
-	descr->status = 0;
+	/* Start with the first descriptor in the list */
+	info->cur_rec_descr = 0;
 
-	*info->ifirstadr = virt_to_phys(descr);
+	/* Start the DMA */
+	*info->ifirstadr = virt_to_phys(&descr[info->cur_rec_descr]);
 	*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, start);
 
-#ifdef SERIAL_HANDLE_EARLY_ERRORS
-	e100_enable_serial_data_irq(info);
-#endif	
-	/* input dma should be running now */
-
-	/* unthrottle if we have throttled */
-	if (E100_RTS_GET(info))
-		tty->driver.unthrottle(info->tty);
+	/* Input DMA should be running now */
+	return 1;
 }
 
 static void 
 start_receive(struct e100_serial *info)
 {
-	struct etrax_dma_descr *descr;
-	
 #ifdef CONFIG_SVINTO_SIM
 	/* No receive in the simulator.  Will probably be when the rest of
 	 * the serial interface works, and this piece will just be removed.
@@ -1410,21 +1459,10 @@
 	while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) ==
 	       IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
 
-	descr = &info->rec_descr;
-	
-	/* start the receiving dma into the flip buffer */
-	
-	descr->ctrl = d_int | d_eol | d_eop;
-	descr->sw_len = TTY_FLIPBUF_SIZE;
-	descr->buf = virt_to_phys(info->tty->flip.char_buf_ptr);
-	descr->hw_len = 0;
-	descr->status = 0;
-	
 	info->tty->flip.count = 0;
 
-	*info->ifirstadr = virt_to_phys(descr);
-	*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, start);
-
+	start_recv_dma(info);
+	
 #ifdef CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST
 	start_flush_timer();
 #endif
@@ -1474,9 +1512,16 @@
 		info = rs_table + i;
 		if (!info->uses_dma) 
 			continue; 
-		/* check for dma_descr (dont need to check for dma_eop in output dma for serial */
+		/* check for dma_descr (don't need to check for dma_eop in output dma for serial */
 		if (ireg & info->irq) {  
 			/* we can send a new dma bunch. make it so. */
+			DEBUG_LOG(info->line, "tr_interrupt %i\n", i);
+			/* Read jiffies_usec first, 
+			 * we want this time to be as late as possible
+			 */
+ 			PROCSTAT(ser_stat[info->line].tx_dma_ints++);
+			info->last_tx_active_usec = GET_JIFFIES_USEC();
+			info->last_tx_active = jiffies;
 			transmit_chars(info);
 		}
 		
@@ -1524,77 +1569,26 @@
 	}
 }
 
-#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
-static int serial_fast_timer_started = 0;
-static int serial_fast_timer_expired = 0;
-static void flush_timeout_function(unsigned long data);
-#define START_FLUSH_FAST_TIMER(info, string) {\
-  unsigned long timer_flags; \
-  save_flags(timer_flags); \
-  cli(); \
-  TIMERD(DEBUG_LOG(info->line, "start_timer? %i ", info->line)); \
-  if (fast_timers[info->line].function == NULL) { \
-    serial_fast_timer_started++; \
-    TIMERD(DEBUG_LOG(info->line, "start_timer %i ", info->line)); \
-    TIMERD(DEBUG_LOG(info->line, "num started: %i\n", serial_fast_timer_started)); \
-    start_one_shot_timer(&fast_timers[info->line], \
-                         flush_timeout_function, \
-                         (unsigned long)info, \
-                         info->char_time_usec*4, \
-                         string); \
-  } \
-  else { \
-    /* DEBUG_LOG(info->line, " ## timer %i running ##\n", info->line); */ \
-  } \
-  restore_flags(timer_flags); \
-}
-
-#else
-#define START_FLUSH_FAST_TIMER(info, string)
-#endif
-
-void _INLINE_ check_flush_timeout(struct e100_serial *info)
+static _INLINE_ int
+force_eop_if_needed(struct e100_serial *info)
 {
-	unsigned char rstat;
-	unsigned int magic;
-
-	if (0 /*info->tty->processing_flip*/) {
-		if (!E100_RTS_GET(info)) {
-			int left = (*info->ihwswadr >> 16) - (*info->istatusadr & 0x3F);
-
-			if (left < TTY_THROTTLE_LIMIT)
-				info->tty->driver.throttle(info->tty);
-		}
-
-		PROCSTAT(ser_stat[info->line].processing_flip++);
-		START_FLUSH_FAST_TIMER(info, "flip");
-		return;
-	}
-
 	/* We check data_avail bit to determine if data has 
 	 * arrived since last time
 	 */ 
-	magic = info->fifo_magic;
-#ifdef SERIAL_DEBUG_DATA
-	if (info->fifo_magic || info->fifo_didmagic) {
-		DEBUG_LOG(info->line, "timeout_int: did fifo_magic %03X\n",
-		    (info->fifo_didmagic << 8) | info->fifo_magic);
-	}
-#endif
-	rstat = info->port[REG_STATUS];
+	unsigned char rstat = info->port[REG_STATUS];
+
 	/* error or datavail? */
 	if (rstat & SER_ERROR_MASK) { 
-		/* Some error has occured */
-		/* If there has been valid data, 
-		 * an EOP interrupt will be made automatically.
-		 * If no data, the normal ser_interrupt should be enabled 
-		 * and handle it.
+		/* Some error has occurred. If there has been valid data, an
+		 * EOP interrupt will be made automatically. If no data, the
+		 * normal ser_interrupt should be enabled and handle it.
 		 * So do nothing!
 		 */
 		DEBUG_LOG(info->line, "timeout err: rstat 0x%03X\n",
 		          rstat | (info->line << 8));
-		return;
+		return 0;
 	}
+
 	if (rstat & SER_DATA_AVAIL_MASK) { 
 		/* Ok data, no error, count it */
 		TIMERD(DEBUG_LOG(info->line, "timeout: rstat 0x%03X\n",
@@ -1602,32 +1596,87 @@
 		/* Read data to clear status flags */
 		(void)info->port[REG_DATA];
 
-		magic++;
+		info->forced_eop = 0;
+		START_FLUSH_FAST_TIMER(info, "magic");
+		return 0;
 	}
 
-	if (magic != info->fifo_magic) {
-		info->fifo_magic = magic;
-		info->fifo_didmagic = 0;
-		START_FLUSH_FAST_TIMER(info, "magic");
-	} else {
-		/* hit the timeout, force an EOP for the input
-		 * dma channel if we haven't already
-		 */
-		if (!info->fifo_didmagic && magic) {
-			info->fifo_didmagic = 1;
-			info->fifo_magic = 0;
-			PROCSTAT(ser_stat[info->line].timeout_flush_cnt++);
-			DEBUG_LOG(info->line, "timeout EOP %i\n", info->line);
-			TIMERD(DEBUG_LOG(info->line, "timeout magic %i\n", magic));
-			FORCE_EOP(info);
-		}
+	/* hit the timeout, force an EOP for the input
+	 * dma channel if we haven't already
+	 */
+	if (!info->forced_eop) {
+		info->forced_eop = 1;
+		PROCSTAT(ser_stat[info->line].timeout_flush_cnt++);
+		DEBUG_LOG(info->line, "timeout EOP %i\n", info->line);
+		FORCE_EOP(info);
 	}
-} /* check_flush_timeout */
+
+	return 1;
+}
+
+static _INLINE_ void
+flush_to_flip_buffer(struct e100_serial *info)
+{
+	struct tty_struct *tty = info->tty;
+	unsigned int count = CIRC_CNT_TO_END(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE);
+	unsigned int length;
+	unsigned long flags;
+
+	if (!count)
+		return;
+
+	save_flags(flags);
+	cli();
+
+	length = tty->flip.count;
+	
+	do {
+		if (length + count > TTY_FLIPBUF_SIZE)
+			count = TTY_FLIPBUF_SIZE - length;
+
+		memcpy(tty->flip.char_buf_ptr + length, info->recv.buf + info->recv.tail, count);
+		memcpy(tty->flip.flag_buf_ptr + length, info->flag_buf + info->recv.tail, count);
+		info->recv.tail = ((info->recv.tail + count) & (SERIAL_RECV_SIZE-1));
+		length += count;
+		
+		count = CIRC_CNT_TO_END(info->recv.head,
+					info->recv.tail,
+					SERIAL_RECV_SIZE);
+	} while (length < TTY_FLIPBUF_SIZE && count);
+
+	tty->flip.count = length;
+
+	restore_flags(flags);
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,66)
+	/* this includes a check for low-latency */
+	tty_flip_buffer_push(tty);
+#else
+	queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
+#endif
+
+	/* unthrottle if we have throttled */
+	if (E100_RTS_GET(info) &&
+	    CIRC_SPACE(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE) > TTY_THROTTLE_LIMIT)
+		tty->driver.unthrottle(info->tty);
+}
+
+static _INLINE_ void
+check_flush_timeout(struct e100_serial *info)
+{
+	force_eop_if_needed(info);
+
+	flush_to_flip_buffer(info);
+
+	if (CIRC_CNT(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE))
+		START_FLUSH_FAST_TIMER(info, "flip");
+}
 
 #ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
 static void flush_timeout_function(unsigned long data)
 {
 	struct e100_serial *info = (struct e100_serial *)data;
+
 	fast_timers[info->line].function = NULL;
 	serial_fast_timer_expired++;
 	TIMERD(DEBUG_LOG(info->line, "flush_timout %i ", info->line));
@@ -1681,7 +1730,6 @@
 {
 	struct e100_serial *info;
 	int i;
-	unsigned int magic;
 
 #ifdef CONFIG_SVINTO_SIM
 	return;
@@ -1689,35 +1737,8 @@
 	
 	for (i = 0; i < NR_PORTS; i++) {
 		info = rs_table + i;
-		if (!info->enabled || !(info->flags & ASYNC_INITIALIZED))
-			continue;
-
-		/* istatusadr (bit 6-0) hold number of bytes in fifo 
-		 * ihwswadr (bit 31-16) holds number of bytes in dma buffer
-		 * ihwswadr (bit 15-0) specifies size of dma buffer
-		 */
-
-		magic = (*info->istatusadr & 0x3f);
-		magic += ((*info->ihwswadr & 0xffff) - (*info->ihwswadr >> 16));
-
-		/* if magic is equal to fifo_magic (magic in previous
-		 * timeout_interrupt) then no new data has arrived since last
-		 * interrupt and we'll force eop to flush fifo+dma buffers
-		 */
-
-		if (magic != info->fifo_magic) {
-			info->fifo_magic = magic;
-			info->fifo_didmagic = 0;
-		} else {
-			/* hit the timeout, force an EOP for the input
-			 * dma channel if we haven't already
-			 */
-			if (!info->fifo_didmagic && magic) {
-				info->fifo_didmagic = 1;
-				info->fifo_magic = 0;
-				FORCE_EOP(info);
-			}
-		}
+		if (info->uses_dma) 
+			check_flush_timeout(info);
 	}
 
 	/* restart flush timer */
@@ -1770,7 +1791,7 @@
 
 Multiple frame errors with data == 0x00 (B),
 but the part of the break trigs is interpreted as a start bit (and possibly
-som 0 bits followed by a number of 1 bits and a stop bit).
+some 0 bits followed by a number of 1 bits and a stop bit).
 Depending on parity settings etc. this last character can be either
 a fake "valid" char (F) or have a parity error (E).
 
@@ -1805,19 +1826,20 @@
 /*	DEBUG_LOG(info->line, "ser_interrupt stat %03X\n", rstat | (i << 8)); */
 	if (rstat & SER_ERROR_MASK) {
 		unsigned char data;
+
 		info->last_rx_active_usec = GET_JIFFIES_USEC();
 		info->last_rx_active = jiffies;
-		/* if we got an error, we must reset it by
-		 * reading the data_in field
+		/* If we got an error, we must reset it by reading the
+		 * data_in field
 		 */
 		data = info->port[REG_DATA];
 
-		if ((data == 0x00) && (rstat & SER_FRAMING_ERR_MASK)) {
-			/* Most likely a break, but we get 
-			 * interrupts over and over again.
+		if (!data && (rstat & SER_FRAMING_ERR_MASK)) {
+			/* Most likely a break, but we get interrupts over and
+			 * over again.
 			 */
 
-			if (info->break_detected_cnt == 0) {
+			if (!info->break_detected_cnt) {
 				DEBUG_LOG(info->line, "#BRK start\n", 0);
 			}
 			if (rstat & SER_RXD_MASK) {
@@ -1833,46 +1855,46 @@
 			}
 			info->break_detected_cnt++;
 		} else {
-			/* Error doesn't look like a break,
-			 * but could be end of a break 
+			/* The error does not look like a break, but could be
+			 * the end of one
 			 */
 			if (info->break_detected_cnt) {
 				DEBUG_LOG(info->line, "EBRK %i\n", info->break_detected_cnt);
 				info->errorcode = ERRCODE_INSERT_BREAK;
+			} else {
+				if (info->errorcode == ERRCODE_INSERT_BREAK)
+					add_char_and_flag(info, '\0', TTY_BREAK);
+
+				if (rstat & SER_PAR_ERR_MASK)
+					add_char_and_flag(info, data, TTY_PARITY);
+				else if (rstat & SER_OVERRUN_MASK)
+					add_char_and_flag(info, data, TTY_OVERRUN);
+				else if (rstat & SER_FRAMING_ERR_MASK)
+					add_char_and_flag(info, data, TTY_FRAME);
+				info->errorcode = 0;
 			}
 			info->break_detected_cnt = 0;
 			DEBUG_LOG(info->line, "#iERR s d %04X\n",
 				  ((rstat & SER_ERROR_MASK) << 8) | data);
 		}
 		PROCSTAT(ser_stat[info->line].early_errors_cnt++);
-
-#if 0
-		/* Reset DMA before starting */
-		*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
-		while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) ==
-		       IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
-#endif
-	} else { /* it was a valid byte, now let the dma do the rest */
-		unsigned char data;
+	} else { /* It was a valid byte, now let the DMA do the rest */
 		unsigned long curr_time_u = GET_JIFFIES_USEC();
 		unsigned long curr_time = jiffies;
 		
 		if (info->break_detected_cnt) {
-			/* Detect if this character is a new
-			 * valid char or the last char in a
-			 * break sequence:
-			 * If LSBits are 0 and MSBits are high
-			 * AND the time is close to the
-			 * previous interrupt we should discard
-			 * it.
+			/* Detect if this character is a new valid char or the
+			 * last char in a break sequence: If LSBits are 0 and
+			 * MSBits are high AND the time is close to the
+			 * previous interrupt we should discard it.
 			 */
 			long elapsed_usec = 
-			  (curr_time - info->last_rx_active) * (1000000/HZ) + 
-			  curr_time_u - info->last_rx_active_usec;
-			if (elapsed_usec<2*info->char_time_usec) {
+				(curr_time - info->last_rx_active) * (1000000/HZ) + 
+				curr_time_u - info->last_rx_active_usec;
+			if (elapsed_usec < 2*info->char_time_usec) {
 				DEBUG_LOG(info->line, "FBRK %i\n", info->line);
-				/* Report as BREAK (error) and 
-				 * let receive_chars handle it
+				/* Report as BREAK (error) and let
+				 * receive_chars() handle it
 				 */
 				info->errorcode = ERRCODE_SET_BREAK;
 			} else {
@@ -1880,22 +1902,19 @@
 			}
 			DEBUG_LOG(info->line, "num brk %i\n", info->break_detected_cnt);
 		}
-		/* Reset data_avail by
-		 * reading the data_in field
-		 */
-		data = info->port[REG_DATA];
-		info->break_detected_cnt = 0;
-		info->fifo_magic++; /* Count received chars */
+
 #ifdef SERIAL_DEBUG_INTR
 		printk("** OK, disabling ser_interupts\n");
 #endif
-		PROCSTAT(ser_stat[info->line].ser_ints_ok_cnt++);
-		DEBUG_LOG(info->line, " ser_int OK %03X\n",
-		          (info->line << 8) | data);
 		e100_disable_serial_data_irq(info);
+
+		info->break_detected_cnt = 0;
+
+		PROCSTAT(ser_stat[info->line].ser_ints_ok_cnt++);
+		DEBUG_LOG(info->line, "ser_int OK %d\n", info->line);
 	}
 
-	/* restart the DMA never hurts */
+	/* Restarting the DMA never hurts */
 	*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, restart);
 	START_FLUSH_FAST_TIMER(info, "ser_int");
 } /* handle_ser_interrupt */
@@ -1973,7 +1992,7 @@
 {
 	struct e100_serial	*info = (struct e100_serial *) private_;
 	struct tty_struct	*tty;
-	
+
 	tty = info->tty;
 	if (!tty)
 		return;
@@ -1985,43 +2004,46 @@
 startup(struct e100_serial * info)
 {
 	unsigned long flags;
-	unsigned long page;
+	unsigned long xmit_page;
+	unsigned char *recv_page;
+
+	xmit_page = get_zeroed_page(GFP_KERNEL);
+	if (!xmit_page)
+		return -ENOMEM;
 
-	page = get_zeroed_page(GFP_KERNEL);
-	if (!page)
+	recv_page = kmalloc(2 * SERIAL_RECV_SIZE + SERIAL_RECV_DESCRIPTORS * SERIAL_DESCR_BUF_SIZE, GFP_KERNEL);
+	if (!recv_page) {
+		free_page(xmit_page);
 		return -ENOMEM;
+	}
 
 	save_flags(flags); cli();
 
 	/* if it was already initialized, skip this */
-	
+
 	if (info->flags & ASYNC_INITIALIZED) {
-		free_page(page);
 		restore_flags(flags);
+		free_page(xmit_page);
+		kfree(recv_page);
 		return 0;
 	}
-	
+
 	if (info->xmit.buf)
-		free_page(page);
+		free_page(xmit_page);
 	else
-		info->xmit.buf = (unsigned char *) page;
-		
-#ifdef SERIAL_DEBUG_OPEN
-	printk("starting up ttyS%d (xmit_buf 0x%x)...\n", info->line, info->xmit.buf);
-#endif
-
-	if (info->tty) {
-
-		/* clear the tty flip flag buffer since we will not
-		 * be using it (we only use the first byte..)
-		 */
+		info->xmit.buf = (unsigned char *) xmit_page;
 
-		memset(info->tty->flip.flag_buf, 0, TTY_FLIPBUF_SIZE * 2);
+	if (info->recv.buf)
+		kfree(recv_page);
+	else {
+		info->recv.buf = (unsigned char *) recv_page;
+		info->flag_buf = info->recv.buf + SERIAL_RECV_SIZE;
 	}
 
-	save_flags(flags);
-	cli();
-	
+#ifdef SERIAL_DEBUG_OPEN
+	printk("starting up ttyS%d (xmit_buf 0x%p, recv_buf 0x%p)...\n", info->line, info->xmit.buf, info->recv.buf);
+#endif
+
 #ifdef CONFIG_SVINTO_SIM
 	/* Bits and pieces collected from below.  Better to have them
 	   in one ifdef:ed clause than to mix in a lot of ifdefs,
@@ -2029,7 +2051,8 @@
 	if (info->tty)
 		clear_bit(TTY_IO_ERROR, &info->tty->flags);
 	info->xmit.head = info->xmit.tail = 0;
-	
+	info->recv.head = info->recv.tail = 0;
+
 	/* No real action in the simulator, but may set info important
 	   to ioctl. */
 	change_speed(info);
@@ -2039,33 +2062,35 @@
 	 * Clear the FIFO buffers and disable them
 	 * (they will be reenabled in change_speed())
 	 */
-	
+
 	/*
 	 * Reset the DMA channels and make sure their interrupts are cleared
 	 */
-	
+
 	info->uses_dma = 1;
 	*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
 	*info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
 
-	/* wait until reset cycle is complete */
+	/* Wait until reset cycle is complete */
 	while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) ==
 	       IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
 
 	while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->ocmdadr) ==
 	       IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
-	
+
+	/* Make sure the irqs are cleared */
 	*info->iclrintradr =
 		IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
 		IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
 	*info->oclrintradr =
 		IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
 		IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
-	
+
 	if (info->tty)
 		clear_bit(TTY_IO_ERROR, &info->tty->flags);
 
-        info->xmit.head = info->xmit.tail = 0;
+	info->xmit.head = info->xmit.tail = 0;
+	info->recv.head = info->recv.tail = 0;
 	
 	/*
 	 * and set the speed and other flags of the serial port
@@ -2085,7 +2110,7 @@
 	e100_enable_txdma_irq(info);
 	e100_enable_rxdma_irq(info);
 
-	info->tr_running = 0;  /* to be sure we dont lock up the transmitter */
+	info->tr_running = 0; /* to be sure we don't lock up the transmitter */
 
 	/* setup the dma input descriptor and start dma */
 	
@@ -2120,7 +2145,7 @@
 	unsigned long flags;
 
 #ifndef CONFIG_SVINTO_SIM	
-	/* shut down the transmitter and receiver  */
+	/* shut down the transmitter and receiver */
 
 	e100_disable_rx(info);
 	info->port[REG_TR_CTRL] = (info->tx_ctrl &= ~0x40);
@@ -2150,9 +2175,14 @@
 	cli(); /* Disable interrupts */
 	
 	if (info->xmit.buf) {
-		unsigned long pg = (unsigned long) info->xmit.buf;
-		info->xmit.buf = 0;
-		free_page(pg);
+		free_page((unsigned long)info->xmit.buf);
+		info->xmit.buf = NULL;
+	}
+
+	if (info->recv.buf) {
+		kfree(info->recv.buf);
+		info->recv.buf = NULL;
+		info->flag_buf = NULL;
 	}
 
 	if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
@@ -2258,11 +2288,11 @@
 	struct e100_serial *info = (struct e100_serial *)tty->driver_data;
 	unsigned long flags;
 
-	if (info->tr_running
-	    || info->xmit.head == info->xmit.tail
-	    || tty->stopped
-	    || tty->hw_stopped
-	    || !info->xmit.buf)
+	if (info->tr_running ||
+	    info->xmit.head == info->xmit.tail ||
+	    tty->stopped ||
+	    tty->hw_stopped ||
+	    !info->xmit.buf)
 		return;
 
 #ifdef SERIAL_DEBUG_FLOW
@@ -2299,7 +2329,7 @@
 #ifdef CONFIG_SVINTO_SIM
 	/* Really simple.  The output is here and now. */
 	SIMCOUT(buf, count);
-	return;
+	return count;
 #endif
 	save_flags(flags);
 	
@@ -2370,8 +2400,8 @@
 	 * the IRQ's are not running anyway for this port.
 	 */
 	
-	if (info->xmit.head != info->xmit.tail
-	    && !tty->stopped &&
+	if (info->xmit.head != info->xmit.tail &&
+	    !tty->stopped &&
 	    !tty->hw_stopped &&
 	    !info->tr_running) {
 		start_transmit(info);
@@ -2528,20 +2558,20 @@
 	tmp.flags = info->flags;
 	tmp.close_delay = info->close_delay;
 	tmp.closing_wait = info->closing_wait;
-	if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
+	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
 		return -EFAULT;
 	return 0;
 }
 
 static int
-set_serial_info(struct e100_serial * info,
-		struct serial_struct * new_info)
+set_serial_info(struct e100_serial *info,
+		struct serial_struct *new_info)
 {
 	struct serial_struct new_serial;
 	struct e100_serial old_info;
-	int 			retval = 0;
+	int retval = 0;
 
-	if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
+	if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
 		return -EFAULT;
 
 	old_info = *info;
@@ -2636,6 +2666,7 @@
 char *get_control_state_str(int MLines, char *s)
 {
 	int i = 0;
+
 	s[0]='\0';
 	while (control_state_str[i].str != NULL) {
 		if (MLines & control_state_str[i].state) {
@@ -2805,9 +2836,13 @@
 rs_ioctl(struct tty_struct *tty, struct file * file,
 	 unsigned int cmd, unsigned long arg)
 {
-	int error;
 	struct e100_serial * info = (struct e100_serial *)tty->driver_data;
+#if defined(CONFIG_ETRAX_RS485) || (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */
+	int error;
+#endif
+#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */
 	int retval;
+#endif
 	
 	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
 	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD)  &&
@@ -3108,7 +3143,7 @@
 		struct e100_serial *info)
 {
 	DECLARE_WAITQUEUE(wait, current);
-	unsigned long   flags;
+	unsigned long	flags;
 	int		retval;
 	int		do_clocal = 0, extra_count = 0;
 	
@@ -3292,7 +3327,7 @@
 	}
 
 	/*
-	 * If the port is the middle of closing, bail out now
+	 * If the port is in the middle of closing, bail out now
 	 */
 	if (tty_hung_up_p(filp) ||
 	    (info->flags & ASYNC_CLOSING)) {
@@ -3346,12 +3381,11 @@
 
 static inline int line_info(char *buf, struct e100_serial *info)
 {
-	char	stat_buf[30], control, status;
+	char	stat_buf[30];
 	int	ret;
-	unsigned long flags;
 
 	ret = sprintf(buf, "%d: uart:E100 port:%lX irq:%d",
-		      info->line, info->port, info->irq);
+		      info->line, (unsigned long)info->port, info->irq);
 
 	if (!info->port || (info->type == PORT_UNKNOWN)) {
 		ret += sprintf(buf+ret, "\n");
@@ -3376,19 +3410,24 @@
 	ret += sprintf(buf+ret, " baud:%d", info->baud);
 
 	ret += sprintf(buf+ret, " tx:%lu rx:%lu",
-		       info->icount.tx, info->icount.rx);
+		       (unsigned long)info->icount.tx,
+		       (unsigned long)info->icount.rx);
 
 	if (info->icount.frame)
-		ret += sprintf(buf+ret, " fe:%lu", info->icount.frame);
+		ret += sprintf(buf+ret, " fe:%lu",
+			       (unsigned long)info->icount.frame);
 	
 	if (info->icount.parity)
-		ret += sprintf(buf+ret, " pe:%lu", info->icount.parity);
+		ret += sprintf(buf+ret, " pe:%lu",
+			       (unsigned long)info->icount.parity);
 	
 	if (info->icount.brk)
-		ret += sprintf(buf+ret, " brk:%lu", info->icount.brk);	
+		ret += sprintf(buf+ret, " brk:%lu",
+			       (unsigned long)info->icount.brk);	
 
 	if (info->icount.overrun)
-		ret += sprintf(buf+ret, " oe:%lu", info->icount.overrun);
+		ret += sprintf(buf+ret, " oe:%lu",
+			       (unsigned long)info->icount.overrun);
 
 	/*
 	 * Last thing is the RS-232 status lines
@@ -3526,8 +3565,7 @@
 		info->tty = 0;
 		info->type = PORT_ETRAX;
 		info->tr_running = 0;
-		info->fifo_magic = 0;
-		info->fifo_didmagic = 0;
+		info->forced_eop = 0;
 		info->flags = 0;
 		info->close_delay = 5*HZ/10;
 		info->closing_wait = 30*HZ;
@@ -3541,8 +3579,21 @@
 		info->normal_termios = serial_driver.init_termios;
 		init_waitqueue_head(&info->open_wait);
 		init_waitqueue_head(&info->close_wait);
-		info->xmit.buf = 0;
+		info->xmit.buf = NULL;
 		info->xmit.tail = info->xmit.head = 0;
+		info->recv.buf = NULL;
+		info->recv.tail = info->recv.head = 0;
+		info->flag_buf = NULL;
+		info->last_tx_active_usec = 0;
+		info->last_tx_active = 0;
+
+#if defined(CONFIG_ETRAX_RS485)
+		/* Set sane defaults */
+		info->rs485.rts_on_send = 0;
+		info->rs485.rts_after_sent = 1;
+		info->rs485.delay_rts_before_send = 0;
+		info->rs485.enabled = 0;
+#endif
 
 		if (info->enabled) {
 			printk(KERN_INFO "%s%d at 0x%x is a builtin UART with DMA\n",
diff -urN linux-2.4.18-pre2/arch/cris/drivers/serial.h linux/arch/cris/drivers/serial.h
--- linux-2.4.18-pre2/arch/cris/drivers/serial.h	Fri Nov  9 13:58:02 2001
+++ linux/arch/cris/drivers/serial.h	Thu Jan 10 13:28:32 2002
@@ -23,81 +23,90 @@
  * For definitions of the flags field, see tty.h
  */
 
+#define SERIAL_RECV_DESCRIPTORS 8
+
 struct e100_serial {
-	int                   baud;
-	volatile u8 *port;             /* R_SERIALx_CTRL */
-	u32 irq;                       /* bitnr in R_IRQ_MASK2 for dmaX_descr */
-
-	volatile u8 *oclrintradr;      /* adr to R_DMA_CHx_CLR_INTR, output */
-	volatile u32 *ofirstadr;       /* adr to R_DMA_CHx_FIRST, output */
-	volatile u8 *ocmdadr;          /* adr to R_DMA_CHx_CMD, output */
-	const volatile u8 *ostatusadr; /* adr to R_DMA_CHx_STATUS, output */
-	volatile u32 *ohwswadr;        /* adr to R_DMA_CHx_HWSW, output */
-
-	volatile u8 *iclrintradr;      /* adr to R_DMA_CHx_CLR_INTR, input */
-	volatile u32 *ifirstadr;       /* adr to R_DMA_CHx_FIRST, input */
-	volatile u8 *icmdadr;          /* adr to R_DMA_CHx_CMD, input */
-	const volatile u8 *istatusadr; /* adr to R_DMA_CHx_STATUS, input */
-	volatile u32 *ihwswadr;        /* adr to R_DMA_CHx_HWSW, input */
-
-	int			flags; 		/* defined in tty.h */
-
-	u8           rx_ctrl; /* shadow for R_SERIALx_REC_CTRL */
-	u8           tx_ctrl; /* shadow for R_SERIALx_TR_CTRL */
-	u8           iseteop; /* bit number for R_SET_EOP for the input dma */
-	int          enabled;    /* Set to 1 if the port is enabled in HW config */
-  
+	int			baud;
+	volatile u8		*port; /* R_SERIALx_CTRL */
+	u32			irq;  /* bitnr in R_IRQ_MASK2 for dmaX_descr */
+
+	/* Output registers */
+	volatile u8		*oclrintradr; /* adr to R_DMA_CHx_CLR_INTR */
+	volatile u32		*ofirstadr;   /* adr to R_DMA_CHx_FIRST */
+	volatile u8		*ocmdadr;     /* adr to R_DMA_CHx_CMD */
+	const volatile u8	*ostatusadr;  /* adr to R_DMA_CHx_STATUS */
+	volatile u32		*ohwswadr;    /* adr to R_DMA_CHx_HWSW */
+	volatile u32		*odescradr;   /* adr to R_DMA_CHx_DESCR */
+
+	/* Input registers */
+	volatile u8		*iclrintradr; /* adr to R_DMA_CHx_CLR_INTR */
+	volatile u32		*ifirstadr;   /* adr to R_DMA_CHx_FIRST */
+	volatile u8		*icmdadr;     /* adr to R_DMA_CHx_CMD */
+	const volatile u8	*istatusadr;  /* adr to R_DMA_CHx_STATUS */
+	volatile u32		*ihwswadr;    /* adr to R_DMA_CHx_HWSW */
+	volatile u32		*idescradr;   /* adr to R_DMA_CHx_DESCR */
+
+	int			flags;	/* defined in tty.h */
+
+	u8			rx_ctrl; /* shadow for R_SERIALx_REC_CTRL */
+	u8			tx_ctrl; /* shadow for R_SERIALx_TR_CTRL */
+	u8			iseteop; /* bit number for R_SET_EOP for the input dma */
+
+	int			enabled; /* Set to 1 if the port is enabled in HW config */
   
-/* end of fields defined in rs_table[] in .c-file */
-	int          uses_dma; /* Set to 1 if DMA should be used */
-	unsigned char           fifo_didmagic; /* a fifo eop has been forced */
+	/* end of fields defined in rs_table[] in .c-file */
 
-	struct etrax_dma_descr tr_descr, rec_descr;
+	int			uses_dma; /* Set to 1 if DMA should be used */
+	unsigned char           forced_eop; /* a fifo eop has been forced */
 
-	int                     fifo_magic; /* fifo amount - bytes left in dma buffer */
+	struct etrax_dma_descr	tr_descr;
+	struct etrax_dma_descr	rec_descr[SERIAL_RECV_DESCRIPTORS];
+	int			cur_rec_descr;
 
-	volatile int            tr_running; /* 1 if output is running */
+	volatile int		tr_running; /* 1 if output is running */
 
-	struct tty_struct 	*tty;
+	struct tty_struct	*tty;
 	int			read_status_mask;
 	int			ignore_status_mask;
 	int			x_char;	/* xon/xoff character */
 	int			close_delay;
-	unsigned short	        closing_wait;
-	unsigned short	        closing_wait2;
+	unsigned short		closing_wait;
+	unsigned short		closing_wait2;
 	unsigned long		event;
 	unsigned long		last_active;
 	int			line;
-	int                     type;  /* PORT_ETRAX */
+	int			type;  /* PORT_ETRAX */
 	int			count;	    /* # of fd on device */
 	int			blocked_open; /* # of blocked opens */
 	long			session; /* Session of opening process */
 	long			pgrp; /* pgrp of opening process */
-	struct circ_buf         xmit;
+	struct circ_buf		xmit;
+	struct circ_buf		recv;
+	unsigned char		*flag_buf;
 
 	struct tq_struct	tqueue;
-	struct async_icount     icount;   /* error-statistics etc.*/
-	struct termios	        normal_termios;
-	struct termios	        callout_termios;
+	struct async_icount	icount;   /* error-statistics etc.*/
+	struct termios		normal_termios;
+	struct termios		callout_termios;
 #ifdef DECLARE_WAITQUEUE
-	wait_queue_head_t       open_wait;
-        wait_queue_head_t       close_wait;
-#else   
-        struct wait_queue       *open_wait;
-        struct wait_queue       *close_wait;
+	wait_queue_head_t	open_wait;
+	wait_queue_head_t	close_wait;
+#else
+	struct wait_queue	*open_wait;
+	struct wait_queue	*close_wait;
 #endif  
 
-	unsigned long char_time_usec;       /* The time for 1 char, in usecs */
-	unsigned long last_tx_active_usec;  /* Last tx usec in the jiffies */
-	unsigned long last_tx_active;       /* Last tx time in jiffies */
-	unsigned long last_rx_active_usec;  /* Last rx usec in the jiffies */
-	unsigned long last_rx_active;       /* Last rx time in jiffies */
+	unsigned long		char_time_usec;       /* The time for 1 char, in usecs */
+	unsigned long		last_tx_active_usec;  /* Last tx usec in the jiffies */
+	unsigned long		last_tx_active;       /* Last tx time in jiffies */
+	unsigned long		last_rx_active_usec;  /* Last rx usec in the jiffies */
+	unsigned long		last_rx_active;       /* Last rx time in jiffies */
 
-	int break_detected_cnt;
-	int errorcode;
+	int			break_detected_cnt;
+	int			errorcode;
 
 #ifdef CONFIG_RS485
-	struct rs485_control    rs485;  /* RS-485 support */
+	struct rs485_control	rs485;  /* RS-485 support */
 #endif
 };
 
@@ -116,4 +125,4 @@
 
 #endif /* __KERNEL__ */
 
-#endif /* !(_ETRAX_SERIAL_H) */
+#endif /* !_ETRAX_SERIAL_H */
diff -urN linux-2.4.18-pre2/arch/cris/drivers/sync_serial.c linux/arch/cris/drivers/sync_serial.c
--- linux-2.4.18-pre2/arch/cris/drivers/sync_serial.c	Tue May  1 16:04:56 2001
+++ linux/arch/cris/drivers/sync_serial.c	Thu Jan 10 13:28:32 2002
@@ -711,7 +711,7 @@
 {
 	port->out_descr.hw_len = 0;
 	port->out_descr.next = 0;
-	port->out_descr.ctrl = d_int | d_eol | d_eop;
+	port->out_descr.ctrl = d_int | d_eol | d_eop | d_wait;
 	port->out_descr.sw_len = count;
 	port->out_descr.buf = virt_to_phys(port->out_buffer);
 	port->out_descr.status = 0;
diff -urN linux-2.4.18-pre2/arch/cris/drivers/usb-host.c linux/arch/cris/drivers/usb-host.c
--- linux-2.4.18-pre2/arch/cris/drivers/usb-host.c	Mon Oct  8 11:43:54 2001
+++ linux/arch/cris/drivers/usb-host.c	Thu Jan 10 13:28:32 2002
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2001 Axis Communications AB.
  *
- * $Id: usb-host.c,v 1.11 2001/09/26 11:52:16 bjornw Exp $
+ * $Id: usb-host.c,v 1.13 2001/11/13 12:06:17 pkj Exp $
  *
  */
 
@@ -34,7 +34,7 @@
 #define ETRAX_USB_RX_IRQ USB_DMA_RX_IRQ_NBR
 #define ETRAX_USB_TX_IRQ USB_DMA_TX_IRQ_NBR
 
-static const char *usb_hcd_version = "$Revision: 1.11 $";
+static const char *usb_hcd_version = "$Revision: 1.13 $";
 
 #undef KERN_DEBUG
 #define KERN_DEBUG ""
@@ -78,11 +78,11 @@
 #endif
 
 #ifdef USB_DEBUG_TRACE
-#define DBFENTER (printk(KERN_DEBUG __FILE__ ": Entering : " __FUNCTION__ "\n"))
-#define DBFEXIT  (printk(KERN_DEBUG __FILE__ ": Exiting  : " __FUNCTION__ "\n"))
+#define DBFENTER (printk(KERN_DEBUG __FILE__ ": Entering: " __FUNCTION__ "\n"))
+#define DBFEXIT  (printk(KERN_DEBUG __FILE__ ": Exiting:  " __FUNCTION__ "\n"))
 #else
-#define DBFENTER (NULL)
-#define DBFEXIT  (NULL)
+#define DBFENTER do {} while (0)
+#define DBFEXIT  do {} while (0)
 #endif
 
 /*-------------------------------------------------------------------
@@ -163,7 +163,7 @@
 #define CHECK_ALIGN(x) if (((__u32)(x)) & 0x00000003) \
 {panic("Alignment check (DWORD) failed at %s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__);}
 
-static submit_urb_count = 0;
+static unsigned long submit_urb_count = 0;
 
 //#define ETRAX_USB_INTR_IRQ
 //#define ETRAX_USB_INTR_ERROR_FATAL
@@ -178,6 +178,7 @@
 
 static __u32 ep_usage_bitmask;
 static __u32 ep_really_active;
+static __u32 ep_out_traffic;
 
 static unsigned char RxBuf[RX_BUF_SIZE];
 static USB_IN_Desc_t RxDescList[NBR_OF_RX_DESC] __attribute__ ((aligned (4)));
@@ -196,22 +197,25 @@
 static kmem_cache_t *usb_desc_cache;
 static struct usb_bus *etrax_usb_bus;
 
+#ifdef USB_DEBUG_DESC
 static void dump_urb (purb_t purb);
+#endif
 static void init_rx_buffers(void);
 static int etrax_rh_unlink_urb (urb_t *urb);
 static void etrax_rh_send_irq(urb_t *urb);
 static void etrax_rh_init_int_timer(urb_t *urb);
 static void etrax_rh_int_timer_do(unsigned long ptr);
 
-static void etrax_usb_setup_epid(char epid, char devnum, char endpoint,
-				 char packsize, char slow);
-static int etrax_usb_lookup_epid(unsigned char devnum, char endpoint, char slow, int maxp);
+static void etrax_usb_setup_epid(int epid, char devnum, char endpoint,
+				 char packsize, char slow, char out_traffic);
+static int etrax_usb_lookup_epid(unsigned char devnum, char endpoint,
+				 char slow, int maxp, char out_traffic);
 static int etrax_usb_allocate_epid(void);
-static void etrax_usb_free_epid(char epid);
+static void etrax_usb_free_epid(int epid);
 static void cleanup_sb(USB_SB_Desc_t *sb);
 
-static int etrax_usb_do_ctrl_hw_add(urb_t *urb, char epid, char maxlen);
-static int etrax_usb_do_bulk_hw_add(urb_t *urb, char epid, char maxlen);
+static void etrax_usb_do_ctrl_hw_add(urb_t *urb, int epid, char maxlen);
+static void etrax_usb_do_bulk_hw_add(urb_t *urb, int epid, char maxlen);
 
 static int etrax_usb_submit_ctrl_urb(urb_t *urb);
 
@@ -292,10 +296,10 @@
 
 
 #else
-#define dump_urb(...)     (NULL)
-#define dump_ep_desc(...) (NULL)
-#define dump_sb_desc(...) (NULL)
-#define dump_in_desc(...) (NULL)
+#define dump_urb(...)     do {} while (0)
+#define dump_in_desc(...) do {} while (0)
+#define dump_sb_desc(...) do {} while (0)
+#define dump_ep_desc(...) do {} while (0)
 #endif
 
 static void init_rx_buffers(void)
@@ -423,25 +427,19 @@
 
 static int etrax_usb_unlink_intr_urb(urb_t *urb)
 {
-	struct usb_device *usb_dev = urb->dev;
-	etrax_hc_t *hc = usb_dev->bus->hcpriv;
-
 	USB_EP_Desc_t *tmp_ep;
 	USB_EP_Desc_t *first_ep;
 	
 	USB_EP_Desc_t *ep_desc;
-	USB_SB_Desc_t *sb_desc;
 	
-	char epid;
+	int epid;
 	char devnum;
 	char endpoint;
 	char slow;
 	int maxlen;
+	char out_traffic;
 	int i;
 	
-	etrax_urb_priv_t *urb_priv;
-	unsigned long flags;
-	
 	DBFENTER;
 
 	devnum = usb_pipedevice(urb->pipe);
@@ -449,8 +447,9 @@
 	slow = usb_pipeslow(urb->pipe);
 	maxlen = usb_maxpacket(urb->dev, urb->pipe,
 			       usb_pipeout(urb->pipe));
+	out_traffic = usb_pipeout(urb->pipe);
 
-	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen);
+	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen, out_traffic);
 	if (epid == -1) {
 		err("Trying to unlink urb that is not in traffic queue!!");
 		return -1;  /* fix this */
@@ -517,18 +516,16 @@
 	USB_EP_Desc_t *tmp_ep;
 	USB_EP_Desc_t *first_ep;
 	
-	USB_SB_Desc_t *sb_desc;
-	
-	char epid;
+	int epid;
 	char devnum;
 	char endpoint;
 	char maxlen;
+	char out_traffic;
 	char slow;
 	int interval;
 	int i;
 	
 	etrax_urb_priv_t *urb_priv;
-	unsigned long flags;
 	
 	DBFENTER;
 
@@ -536,6 +533,7 @@
 	endpoint = usb_pipeendpoint(urb->pipe);
 	maxlen = usb_maxpacket(urb->dev, urb->pipe,
 			       usb_pipeout(urb->pipe));
+	out_traffic = usb_pipeout(urb->pipe);
 
 	slow = usb_pipeslow(urb->pipe);
 	interval = urb->interval;
@@ -543,7 +541,7 @@
 	dbg_intr("Intr traffic for dev %d, endpoint %d, maxlen %d, slow %d",
 		 devnum, endpoint, maxlen, slow);
 	
-	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen);
+	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen, out_traffic);
 	if (epid == -1) {
 		epid = etrax_usb_allocate_epid();
 		if (epid == -1) {
@@ -552,7 +550,7 @@
 			return -ENOMEM;
 		}
 		/* Now we have to fill in this ep */
-		etrax_usb_setup_epid(epid, devnum, endpoint, maxlen, slow);
+		etrax_usb_setup_epid(epid, devnum, endpoint, maxlen, slow, out_traffic);
 	}
 	/* Ok, now we got valid endpoint, lets insert some traffic */
 
@@ -641,7 +639,7 @@
 }
 
 
-static int handle_intr_transfer_attn(char epid, int status)
+static void handle_intr_transfer_attn(int epid, int status)
 {
 	urb_t *old_urb;
 
@@ -782,7 +780,7 @@
 /*	DBFEXIT; */
 }
 
-static void etrax_usb_setup_epid(char epid, char devnum, char endpoint, char packsize, char slow)
+static void etrax_usb_setup_epid(int epid, char devnum, char endpoint, char packsize, char slow, char out_traffic)
 {
 	unsigned long flags;
 	
@@ -800,8 +798,6 @@
 	}
 	
 	set_bit(epid, (void *)&ep_usage_bitmask);
-	dbg_ep("Setting up ep_id %d with devnum %d, endpoint %d and max_len %d",
-	       epid, devnum, endpoint, packsize);
 	
 	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
 	nop();
@@ -811,12 +807,20 @@
 		IO_FIELD(R_USB_EPT_DATA, max_len, packsize) |
 		IO_FIELD(R_USB_EPT_DATA, low_speed, slow);
 
+	if (out_traffic)
+		set_bit(epid, (void *)&ep_out_traffic);
+	else
+		clear_bit(epid, (void *)&ep_out_traffic);
+
 	restore_flags(flags);
 
+	dbg_ep("Setting up ep_id %d with devnum %d, endpoint %d and max_len %d (%s)",
+	       epid, devnum, endpoint, packsize, out_traffic ? "OUT" : "IN");
+
 	DBFEXIT;
 }
 
-static void etrax_usb_free_epid(char epid)
+static void etrax_usb_free_epid(int epid)
 {
 	unsigned long flags;
 	
@@ -846,7 +850,7 @@
 }
 
 
-static int etrax_usb_lookup_epid(unsigned char devnum, char endpoint, char slow, int maxp)
+static int etrax_usb_lookup_epid(unsigned char devnum, char endpoint, char slow, int maxp, char out_traffic)
 {
 	int i;
 	unsigned long flags;
@@ -855,10 +859,12 @@
 	DBFENTER;
 
 	save_flags(flags);
-	
+	cli();
+
 	/* Skip first ep_id since it is reserved when intr. or iso traffic is used */
 	for (i = 0; i < NBR_OF_EP_DESC; i++) {
-		if (test_bit(i, (void *)&ep_usage_bitmask)) {
+		if (test_bit(i, (void *)&ep_usage_bitmask) &&
+		    test_bit(i, (void *)&ep_out_traffic) == out_traffic) {
 			*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, i);
 			nop();
 			data = *R_USB_EPT_DATA;
@@ -869,8 +875,8 @@
 			    (IO_EXTRACT(R_USB_EPT_DATA, max_len, data) == maxp)) {
 				restore_flags(flags);
 	
-				dbg_ep("Found ep_id %d for devnum %d, endpoint %d",
-				       i, devnum, endpoint);
+				dbg_ep("Found ep_id %d for devnum %d, endpoint %d (%s)",
+				       i, devnum, endpoint, out_traffic ? "OUT" : "IN");
 				DBFEXIT;
 				return i;
 			}
@@ -879,8 +885,8 @@
 
 	restore_flags(flags);
 	
-	dbg_ep("Found no ep_id for devnum %d, endpoint %d",
-	       devnum, endpoint);
+	dbg_ep("Found no ep_id for devnum %d, endpoint %d (%s)",
+	       devnum, endpoint, out_traffic ? "OUT" : "IN");
 	DBFEXIT;
 	return -1;
 }
@@ -906,15 +912,15 @@
 
 static int etrax_usb_submit_bulk_urb(urb_t *urb)
 {
-	char epid;
+	int epid;
 	char devnum;
 	char endpoint;
 	char maxlen;
+	char out_traffic;
 	char slow;
 
 	urb_t *tmp_urb;
 	
-	etrax_urb_priv_t *urb_priv;
 	unsigned long flags;
 	
 	DBFENTER;
@@ -923,9 +929,10 @@
 	endpoint = usb_pipeendpoint(urb->pipe);
 	maxlen = usb_maxpacket(urb->dev, urb->pipe,
 			       usb_pipeout(urb->pipe));
+	out_traffic = usb_pipeout(urb->pipe);
 	slow = usb_pipeslow(urb->pipe);
 	
-	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen);
+	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen, out_traffic);
 	if (epid == -1) {
 		epid = etrax_usb_allocate_epid();
 		if (epid == -1) {
@@ -934,7 +941,7 @@
 			return -ENOMEM;
 		}
 		/* Now we have to fill in this ep */
-		etrax_usb_setup_epid(epid, devnum, endpoint, maxlen, slow);
+		etrax_usb_setup_epid(epid, devnum, endpoint, maxlen, slow, out_traffic);
 	}
 	/* Ok, now we got valid endpoint, lets insert some traffic */
 
@@ -962,14 +969,13 @@
 	return 0;
 }
 
-static int etrax_usb_do_bulk_hw_add(urb_t *urb, char epid, char maxlen)
+static void etrax_usb_do_bulk_hw_add(urb_t *urb, int epid, char maxlen)
 {
 	USB_SB_Desc_t *sb_desc_1;
 
 	etrax_urb_priv_t *urb_priv;
 
 	unsigned long flags;
-	__u32 r_usb_ept_data;
 
 	DBFENTER;
 
@@ -1078,7 +1084,7 @@
 	DBFEXIT;
 }
 
-static int handle_bulk_transfer_attn(char epid, int status)
+static void handle_bulk_transfer_attn(int epid, int status)
 {
 	urb_t *old_urb;
 	etrax_urb_priv_t *hc_priv;
@@ -1129,8 +1135,9 @@
 		usb_settoggle(old_urb->dev, usb_pipeendpoint(old_urb->pipe),
 			      usb_pipeout(old_urb->pipe), toggle);
 	}
+
 	restore_flags(flags);
-	
+
 	/* If there are any more URB's in the list we'd better start sending */
 	if (URB_List[epid]) {
 		etrax_usb_do_bulk_hw_add(URB_List[epid], epid,
@@ -1161,15 +1168,15 @@
 
 static int etrax_usb_submit_ctrl_urb(urb_t *urb)
 {
-	char epid;
+	int epid;
 	char devnum;
 	char endpoint;
 	char maxlen;
+	char out_traffic;
 	char slow;
 
 	urb_t *tmp_urb;
 	
-	etrax_urb_priv_t *urb_priv;
 	unsigned long flags;
 	
 	DBFENTER;
@@ -1178,9 +1185,10 @@
 	endpoint = usb_pipeendpoint(urb->pipe);
 	maxlen = usb_maxpacket(urb->dev, urb->pipe,
 			       usb_pipeout(urb->pipe));
+	out_traffic = usb_pipeout(urb->pipe);
 	slow = usb_pipeslow(urb->pipe);
 	
-	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen);
+	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen, out_traffic);
 	if (epid == -1) {
 		epid = etrax_usb_allocate_epid();
 		if (epid == -1) {
@@ -1189,7 +1197,7 @@
 			return -ENOMEM;
 		}
 		/* Now we have to fill in this ep */
-		etrax_usb_setup_epid(epid, devnum, endpoint, maxlen, slow);
+		etrax_usb_setup_epid(epid, devnum, endpoint, maxlen, slow, out_traffic);
 	}
 	/* Ok, now we got valid endpoint, lets insert some traffic */
 
@@ -1217,7 +1225,7 @@
 	return 0;
 }
 
-static int etrax_usb_do_ctrl_hw_add(urb_t *urb, char epid, char maxlen)
+static void etrax_usb_do_ctrl_hw_add(urb_t *urb, int epid, char maxlen)
 {
 	USB_SB_Desc_t *sb_desc_1;
 	USB_SB_Desc_t *sb_desc_2;
@@ -1226,8 +1234,6 @@
 	etrax_urb_priv_t *urb_priv;
 
 	unsigned long flags;
-	__u32 r_usb_ept_data;
-	
 
 	DBFENTER;
 
@@ -1365,6 +1371,8 @@
 	
 	DBFENTER;
 
+	urb->next = NULL;
+
 	dump_urb(urb);
 	submit_urb_count++;
 	
@@ -1407,8 +1415,7 @@
 {
 	etrax_hc_t *hc = urb->dev->bus->hcpriv;
 	int epid;
-	int pos;
-	int devnum, endpoint, slow, maxlen;
+	int devnum, endpoint, slow, maxlen, out_traffic;
 	etrax_urb_priv_t *hc_priv;
 	unsigned long flags;
 	
@@ -1419,13 +1426,13 @@
 	slow = usb_pipeslow(urb->pipe);
 	maxlen = usb_maxpacket(urb->dev, urb->pipe,
 			       usb_pipeout(urb->pipe));
+	out_traffic = usb_pipeout(urb->pipe);
 
-	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen);
+	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen, out_traffic);
 
 	if (epid == -1)
 		return 0;
 	
-	
 	if (usb_pipedevice(urb->pipe) == hc->rh.devnum) {
 		int ret;
 		ret =  etrax_rh_unlink_urb(urb);
@@ -1449,35 +1456,37 @@
 	
 	for (epid = 0; epid < 32; epid++) {
 		urb_t *u = URB_List[epid];
-		pos = 0;
+		urb_t *prev = NULL;
+		int pos = 0;
 
 		for (; u; u = u->next) {
 			pos++;
 			if (u == urb) {
-				info("Found urb at epid %d, pos %d", epid, pos);
+				if (!prev) {
+					URB_List[epid] = u->next;
+				} else {
+					prev->next = u->next;
+				}
 
-				if (pos == 1) {
+				restore_flags(flags);
+
+				if (!prev) {
 					if (usb_pipetype(u->pipe) == PIPE_CONTROL) {
 						if (TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
 							/* The EP was enabled, disable it and wait */
 							TxCtrlEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
 							while (*R_DMA_CH8_SUB1_EP == virt_to_phys(&TxCtrlEPList[epid]));
 						}
-						
 					} else if (usb_pipetype(u->pipe) == PIPE_BULK) {
 						if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
 							TxBulkEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
 							while (*R_DMA_CH8_SUB0_EP == virt_to_phys(&TxBulkEPList[epid]));
 						}
 					}
-
-					URB_List[epid] = u->next;
-				
-				} else {
-					urb_t *up;
-					for (up = URB_List[epid]; up->next != u; up = up->next);
-					up->next = u->next;
 				}
+
+				info("Found urb at epid %d, pos %d", epid, pos);
+
 				u->status = -ENOENT;
 				if (u->complete) {
 					u->complete(u);
@@ -1486,7 +1495,11 @@
 				hc_priv = (etrax_urb_priv_t *)u->hcpriv;
 				cleanup_sb(hc_priv->first_sb);
 				kfree(hc_priv);
+
+				DBFEXIT;
+				return 0;
 			}
+			prev = u;
 		}
 	}
 
@@ -1519,13 +1532,6 @@
 
 static void etrax_usb_tx_interrupt(int irq, void *vhc, struct pt_regs *regs)
 {
-	etrax_hc_t *hc = (etrax_hc_t *)vhc;
-	int epid;
-	char eol;
-	urb_t *urb;
-	USB_EP_Desc_t *tmp_ep;
-	USB_SB_Desc_t *tmp_sb;
-	
 	DBFENTER;
 
 	if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub0_descr)) {
@@ -1589,8 +1595,8 @@
 			} else {
 				if ((urb_priv->rx_offset + myNextRxDesc->hw_len) >
 				    urb->transfer_buffer_length) {
-					err("Packet (epid: %d) in RX buffer was bigger "
-					    "than the URB has room for !!!", epid);
+					err("Packet (epid: %d) in RX buffer (%d) was bigger "
+					    "than the URB has room for (%d)!!!", epid, urb_priv->rx_offset + myNextRxDesc->hw_len, urb->transfer_buffer_length);
 					goto skip_out;
 				}
 				
@@ -1606,7 +1612,7 @@
 			
 		} else {
 			err("This is almost fatal, inpacket for epid %d which does not exist "
-			    " or is out !!!\nURB was at 0x%08X", epid, urb);
+			    " or is out!!!\nURB was at 0x%08lX", epid, (unsigned long)urb);
 			
 			goto skip_out;
 		}
@@ -1647,7 +1653,7 @@
 
 }
 
-static int handle_control_transfer_attn(char epid, int status)
+static void handle_control_transfer_attn(int epid, int status)
 {
 	urb_t *old_urb;
 	etrax_urb_priv_t *hc_priv;	
@@ -1710,7 +1716,6 @@
 static void etrax_usb_hc_intr_bottom_half(void *data)
 {
 	struct usb_reg_context *reg = (struct usb_reg_context *)data;
-	urb_t *old_urb;
 	
 	int error_code;
 	int epid;
@@ -1804,7 +1809,7 @@
 
 			if (URB_List[epid] == NULL) {
 				err("R_USB_EPT_DATA is 0x%08X", r_usb_ept_data);
-				err("submit urb has been called %d times..", submit_urb_count);
+				err("submit urb has been called %lu times..", submit_urb_count);
 				err("EPID_ATTN for epid %d, with NULL entry in list", epid);
 				return;
 			}
@@ -1822,7 +1827,7 @@
 				if (IO_EXTRACT(R_USB_EPT_DATA, error_count_out, r_usb_ept_data) == 3 ||
 				    IO_EXTRACT(R_USB_EPT_DATA, error_count_in, r_usb_ept_data) == 3) {
 				/* Actually there were transmission errors */
-					warn("Undefined error for endpoint %d", epid);
+					warn("Undefined error for epid %d", epid);
 					if (usb_pipetype(URB_List[epid]->pipe) == PIPE_CONTROL) {
 						handle_control_transfer_attn(epid, -EPROTO);
 					} else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_BULK) {
@@ -1861,7 +1866,7 @@
 				}
 				
 			} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, stall)) {
-				warn("Stall for endpoint %d", epid);
+				warn("Stall for epid %d", epid);
 				if (usb_pipetype(URB_List[epid]->pipe) == PIPE_CONTROL) {
 					handle_control_transfer_attn(epid, -EPIPE);
 				} else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_BULK) {
@@ -1872,10 +1877,10 @@
 				
 				
 			} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, bus_error)) {
-				panic("USB bus error for endpoint %d\n", epid);
+				panic("USB bus error for epid %d\n", epid);
 				
 			} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, buffer_error)) {
-				warn("Buffer error for endpoint %d", epid);
+				warn("Buffer error for epid %d", epid);
 
 				if (usb_pipetype(URB_List[epid]->pipe) == PIPE_CONTROL) {
 					handle_control_transfer_attn(epid, -EPROTO);
@@ -1985,11 +1990,7 @@
 	void *data = urb->transfer_buffer;
 	int leni = urb->transfer_buffer_length;
 	int len = 0;
-	int status = 0;
 	int stat = 0;
-	int i;
-
-	__u16 cstatus;
 
 	__u16 bmRType_bReq;
 	__u16 wValue;
@@ -2281,7 +2282,7 @@
 						   0xff, "ETRAX 100LX",
 						   data, wLength);
 			if (len > 0) {
-				OK(min_t(int, leni, len));
+				OK(min(leni, len));
 			} else 
 				stat = -EPIPE;
 		}
@@ -2306,7 +2307,7 @@
 
 	urb->actual_length = len;
 	urb->status = stat;
-	urb->dev=NULL;
+	urb->dev = NULL;
 	if (urb->complete) {
 		urb->complete (urb);
 	}
@@ -2356,6 +2357,7 @@
 	ep_usage_bitmask = 0;
 	set_bit(0, (void *)&ep_usage_bitmask);
 	ep_really_active = 0;
+	ep_out_traffic = 0;
 
 	memset(URB_List, 0, sizeof(URB_List));
 
diff -urN linux-2.4.18-pre2/arch/cris/kernel/entry.S linux/arch/cris/kernel/entry.S
--- linux-2.4.18-pre2/arch/cris/kernel/entry.S	Fri Nov  9 13:58:02 2001
+++ linux/arch/cris/kernel/entry.S	Thu Jan 10 13:28:32 2002
@@ -1,4 +1,4 @@
-/* $Id: entry.S,v 1.35 2001/10/30 17:10:15 bjornw Exp $
+/* $Id: entry.S,v 1.37 2001/12/07 17:03:55 bjornw Exp $
  *
  *  linux/arch/cris/entry.S
  *
@@ -7,6 +7,15 @@
  *  Authors:	Bjorn Wesen (bjornw@axis.com)
  *
  *  $Log: entry.S,v $
+ *  Revision 1.37  2001/12/07 17:03:55  bjornw
+ *  Call a c-hook called watchdog_bite_hook instead of show_registers directly
+ *
+ *  Revision 1.36  2001/11/22 13:36:36  bjornw
+ *  * In ret_from_intr, check regs->dccr for usermode reentrance instead of
+ *    DCCR explicitely (because the latter might not reflect current reality)
+ *  * In mmu_bus_fault, set $r9 _after_ calling the C-code instead of before
+ *    since $r9 is call-clobbered and is potentially needed afterwards
+ *
  *  Revision 1.35  2001/10/30 17:10:15  bjornw
  *  Add some syscalls
  *
@@ -217,8 +226,11 @@
 	
 ret_from_intr:
 	;; check for resched only if we're going back to user-mode
-
-	move	$ccr, $r0
+	;; this test matches the user_regs(regs) macro
+	;; we cannot simply test $dccr, because that does not necessarily
+	;; reflect what mode we'll return into.
+	
+	move.d	[$sp + LDCCR], $r0; regs->dccr
 	btstq	8, $r0		; U-flag
 	bpl	_Rexit		; go back directly
 	nop
@@ -468,8 +480,6 @@
 	moveq	1, $r10
 	push	$r10		; frametype == 1, BUSFAULT frame type
 
-	moveq	0, $r9		; busfault is equivalent to an irq
-		
 	move.d	$sp, $r10	; pt_regs argument to handle_mmu_bus_fault
 		
 	jsr	handle_mmu_bus_fault  ; in arch/cris/mm/fault.c
@@ -479,6 +489,8 @@
 	;; process due to a SEGV, scheduled due to a page blocking or
 	;; whatever.
 
+	moveq	0, $r9		; busfault is equivalent to an irq
+		
 	ba	ret_from_intr
 	nop
 		
@@ -585,7 +597,7 @@
 	jsr	printk
 
 	move.d	$sp, $r10
-	jsr	show_registers
+	jsr	watchdog_bite_hook
 
 ;; This nop is here so we see the "Watchdog_bite" label in ksymoops dumps
 ;; rather than "spurious_interrupt".
diff -urN linux-2.4.18-pre2/arch/cris/kernel/head.S linux/arch/cris/kernel/head.S
--- linux-2.4.18-pre2/arch/cris/kernel/head.S	Fri Nov  9 13:58:02 2001
+++ linux/arch/cris/kernel/head.S	Thu Jan 10 13:28:32 2002
@@ -1,4 +1,4 @@
-/* $Id: head.S,v 1.41 2001/10/29 14:55:58 pkj Exp $
+/* $Id: head.S,v 1.43 2001/11/08 15:09:43 starvik Exp $
  * 
  * Head of the kernel - alter with care
  *
@@ -7,6 +7,12 @@
  * Authors:	Bjorn Wesen (bjornw@axis.com)
  * 
  * $Log: head.S,v $
+ * Revision 1.43  2001/11/08 15:09:43  starvik
+ * Only start MII clock if Ethernet is configured
+ *
+ * Revision 1.42  2001/11/08 14:37:34  starvik
+ * Start MII clock early to make sure that it is running at tranceiver reset
+ *
  * Revision 1.41  2001/10/29 14:55:58  pkj
  * Corrected pa$r0 to par0.
  *
@@ -156,7 +162,10 @@
 
 #define CRAMFS_MAGIC 0x28cd3d45
 #define RAM_INIT_MAGIC 0x56902387
-		
+
+#define START_ETHERNET_CLOCK IO_STATE(R_NETWORK_GEN_CONFIG, enable, on) |\
+                             IO_STATE(R_NETWORK_GEN_CONFIG, phy, mii_clk)
+				
 	;; exported symbols
 		
 	.globl	etrax_irv
@@ -301,6 +310,12 @@
 	;; after init.
 	.section ".text.init"
 _inflash:
+#ifdef CONFIG_ETRAX_ETHERNET	
+	;; Start MII clock to make sure it is running when tranceiver is reset
+	move.d START_ETHERNET_CLOCK, $r0
+	move.d $r0, [R_NETWORK_GEN_CONFIG]
+#endif
+		
 	;; We need to initialze DRAM registers before we start using the DRAM
 
 	cmp.d	RAM_INIT_MAGIC, $r8	; Already initialized?
diff -urN linux-2.4.18-pre2/arch/cris/kernel/irq.c linux/arch/cris/kernel/irq.c
--- linux-2.4.18-pre2/arch/cris/kernel/irq.c	Thu Jul 26 15:10:06 2001
+++ linux/arch/cris/kernel/irq.c	Thu Jan 10 13:28:32 2002
@@ -1,4 +1,4 @@
-/* $Id: irq.c,v 1.17 2001/07/25 16:08:01 bjornw Exp $
+/* $Id: irq.c,v 1.18 2001/11/21 13:40:18 bjornw Exp $
  *
  *	linux/arch/cris/kernel/irq.c
  *
@@ -137,7 +137,7 @@
 /* IRQ0 and 1 are special traps */
 void hwbreakpoint(void);
 void IRQ1_interrupt(void);
-BUILD_IRQ(2, 0x04)             /* the timer interrupt */
+BUILD_TIMER_IRQ(2, 0x04)       /* the timer interrupt is somewhat special */
 BUILD_IRQ(3, 0x08)
 BUILD_IRQ(4, 0x10)
 BUILD_IRQ(5, 0x20)
diff -urN linux-2.4.18-pre2/arch/cris/kernel/ksyms.c linux/arch/cris/kernel/ksyms.c
--- linux-2.4.18-pre2/arch/cris/kernel/ksyms.c	Fri Nov  9 13:58:02 2001
+++ linux/arch/cris/kernel/ksyms.c	Thu Jan 10 13:28:32 2002
@@ -23,34 +23,45 @@
 
 extern void dump_thread(struct pt_regs *, struct user *);
 extern unsigned long get_cmos_time(void);
+extern void __Udiv(void);
 extern void __ashrdi3(void);
 extern void iounmap(void *addr);
 
-/* platform dependent support */
-
+/* Platform dependent support */
 EXPORT_SYMBOL(dump_thread);
 EXPORT_SYMBOL(enable_irq);
 EXPORT_SYMBOL(disable_irq);
 EXPORT_SYMBOL(kernel_thread);
 EXPORT_SYMBOL(get_cmos_time);
+EXPORT_SYMBOL(loops_per_usec);
 
+/* String functions */
+EXPORT_SYMBOL(memcmp);
+EXPORT_SYMBOL(memmove);
 EXPORT_SYMBOL(strtok);
 EXPORT_SYMBOL(strpbrk);
 EXPORT_SYMBOL(simple_strtol);
 EXPORT_SYMBOL(strstr);
-
+EXPORT_SYMBOL(strcpy);
 EXPORT_SYMBOL(strchr);
 EXPORT_SYMBOL(strcmp);
 EXPORT_SYMBOL(strlen);
 EXPORT_SYMBOL(strncat);
 EXPORT_SYMBOL(strncmp);
+
+/* Math functions */
+EXPORT_SYMBOL(__Udiv);
 EXPORT_SYMBOL(__ashrdi3);
 
+/* Memory functions */
 EXPORT_SYMBOL(__ioremap);
 EXPORT_SYMBOL(iounmap);
 
-/* export shadow registers for the CPU I/O pins */
+/* Semaphore functions */
+EXPORT_SYMBOL(__up);
+EXPORT_SYMBOL(__down);
 
+/* Export shadow registers for the CPU I/O pins */
 EXPORT_SYMBOL(genconfig_shadow);
 EXPORT_SYMBOL(port_pa_data_shadow);
 EXPORT_SYMBOL(port_pa_dir_shadow);
@@ -59,8 +70,7 @@
 EXPORT_SYMBOL(port_pb_config_shadow);
 EXPORT_SYMBOL(port_g_data_shadow);
 
-/* other stuff */
-
+/* Userspace access functions */
 EXPORT_SYMBOL(strncpy_from_user);
 EXPORT_SYMBOL(__strncpy_from_user);
 EXPORT_SYMBOL(__generic_copy_from_user);
@@ -71,8 +81,8 @@
 
 #undef memcpy
 #undef memset
-extern void * memset(void *,int,__kernel_size_t);
-extern void * memcpy(void *,const void *,__kernel_size_t);
+extern void * memset(void *, int, __kernel_size_t);
+extern void * memcpy(void *, const void *, __kernel_size_t);
 EXPORT_SYMBOL_NOVERS(memcpy);
 EXPORT_SYMBOL_NOVERS(memset);
 
diff -urN linux-2.4.18-pre2/arch/cris/kernel/process.c linux/arch/cris/kernel/process.c
--- linux-2.4.18-pre2/arch/cris/kernel/process.c	Fri Nov  9 13:58:02 2001
+++ linux/arch/cris/kernel/process.c	Thu Jan 10 13:28:32 2002
@@ -1,4 +1,4 @@
-/* $Id: process.c,v 1.20 2001/10/03 08:21:39 jonashg Exp $
+/* $Id: process.c,v 1.22 2001/11/13 09:40:43 orjanf Exp $
  * 
  *  linux/arch/cris/kernel/process.c
  *
@@ -8,6 +8,12 @@
  *  Authors:   Bjorn Wesen (bjornw@axis.com)
  *
  *  $Log: process.c,v $
+ *  Revision 1.22  2001/11/13 09:40:43  orjanf
+ *  Added dump_fpu (needed for core dumps).
+ *
+ *  Revision 1.21  2001/11/12 18:26:21  pkj
+ *  Fixed compiler warnings.
+ *
  *  Revision 1.20  2001/10/03 08:21:39  jonashg
  *  cause_of_death does not exist if CONFIG_SVINTO_SIM is defined.
  *
@@ -57,6 +63,7 @@
 #include <linux/slab.h>
 #include <linux/user.h>
 #include <linux/a.out.h>
+#include <linux/elfcore.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 
@@ -77,7 +84,6 @@
  * setup.
  */
 
-static struct vm_area_struct init_mmap = INIT_MMAP;
 static struct fs_struct init_fs = INIT_FS;
 static struct files_struct init_files = INIT_FILES;
 static struct signal_struct init_signals = INIT_SIGNALS;
@@ -136,14 +142,15 @@
 	 * code to know about it than the watchdog handler in entry.S and
 	 * this code, implementing hard reset through the watchdog.
 	 */
+#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM)
 	extern int cause_of_death;
+#endif
 
 	printk("*** HARD RESET ***\n");
 	cli();
 
 #if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM)
 	cause_of_death = 0xbedead;
-
 #else
 	/* Since we dont plan to keep on reseting the watchdog,
 	   the key can be arbitrary hence three */
@@ -244,9 +251,10 @@
  */
 void dump_thread(struct pt_regs * regs, struct user * dump)
 {
-	int i;
 #if 0
-/* changed the size calculations - should hopefully work better. lbt */
+	int i;
+
+	/* changed the size calculations - should hopefully work better. lbt */
 	dump->magic = CMAGIC;
 	dump->start_code = 0;
 	dump->start_stack = regs->esp & ~(PAGE_SIZE - 1);
@@ -264,6 +272,12 @@
 
 	dump->u_fpvalid = dump_fpu (regs, &dump->i387);
 #endif 
+}
+
+/* Fill in the fpu structure for a core dump. */
+int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu)
+{
+        return 0;
 }
 
 /* 
diff -urN linux-2.4.18-pre2/arch/cris/kernel/ptrace.c linux/arch/cris/kernel/ptrace.c
--- linux-2.4.18-pre2/arch/cris/kernel/ptrace.c	Mon Oct  8 11:43:54 2001
+++ linux/arch/cris/kernel/ptrace.c	Thu Jan 10 13:28:32 2002
@@ -8,6 +8,9 @@
  * Authors:   Bjorn Wesen
  *
  * $Log: ptrace.c,v $
+ * Revision 1.8  2001/11/12 18:26:21  pkj
+ * Fixed compiler warnings.
+ *
  * Revision 1.7  2001/09/26 11:53:49  bjornw
  * PTRACE_DETACH works more simple in 2.4.10
  *
@@ -74,8 +77,6 @@
 static inline int put_reg(struct task_struct *task, unsigned int regno,
 			  unsigned long data)
 {
-	unsigned long *addr;
-
 	if (regno == PT_USP)
 		task->thread.usp = data;
 	else if (regno < PT_MAX)
@@ -207,9 +208,7 @@
 			break;
 
 		case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
-		case PTRACE_CONT: { /* restart after signal. */
-			long tmp;
-
+		case PTRACE_CONT: /* restart after signal. */
 			ret = -EIO;
 			if ((unsigned long) data > _NSIG)
 				break;
@@ -222,16 +221,13 @@
 			wake_up_process(child);
 			ret = 0;
 			break;
-		}
 
 /*
  * make the child exit.  Best I can do is send it a sigkill. 
  * perhaps it should be put in the status that it wants to 
  * exit.
  */
-		case PTRACE_KILL: {
-			long tmp;
-
+		case PTRACE_KILL:
 			ret = 0;
 			if (child->state == TASK_ZOMBIE) /* already dead */
 				break;
@@ -239,11 +235,8 @@
 			/* TODO: make sure any pending breakpoint is killed */
 			wake_up_process(child);
 			break;
-		}
-
-		case PTRACE_SINGLESTEP: {  /* set the trap flag. */
-			long tmp;
 
+		case PTRACE_SINGLESTEP: /* set the trap flag. */
 			ret = -EIO;
 			if ((unsigned long) data > _NSIG)
 				break;
@@ -256,7 +249,6 @@
 			wake_up_process(child);
 			ret = 0;
 			break;
-		}
 
 		case PTRACE_DETACH:
 			ret = ptrace_detach(child, data);
diff -urN linux-2.4.18-pre2/arch/cris/kernel/setup.c linux/arch/cris/kernel/setup.c
--- linux-2.4.18-pre2/arch/cris/kernel/setup.c	Fri Nov  9 13:58:02 2001
+++ linux/arch/cris/kernel/setup.c	Thu Jan 10 13:28:32 2002
@@ -1,4 +1,4 @@
-/* $Id: setup.c,v 1.22 2001/10/23 17:42:58 pkj Exp $
+/* $Id: setup.c,v 1.24 2001/12/07 17:03:19 bjornw Exp $
  *
  *  linux/arch/cris/kernel/setup.c
  *
@@ -26,10 +26,12 @@
 #include <linux/config.h>
 #include <linux/init.h>
 #include <linux/bootmem.h>
+#include <linux/seq_file.h>
 
 #include <asm/segment.h>
 #include <asm/system.h>
 #include <asm/smp.h>
+#include <asm/pgtable.h>
 #include <asm/types.h>
 #include <asm/svinto.h>
 
@@ -72,10 +74,10 @@
 void __init 
 setup_arch(char **cmdline_p)
 {
-        unsigned long bootmap_size;
+	extern void init_etrax_debug(void);
+	unsigned long bootmap_size;
 	unsigned long start_pfn, max_pfn;
 	unsigned long memory_start;
-	extern void console_print_etrax(const char *b);
 
  	/* register an initial console printing routine for printk's */
 
@@ -87,12 +89,12 @@
 
 	if(romfs_in_flash || !romfs_length) {
 		/* if we have the romfs in flash, or if there is no rom filesystem,
-		 * our free area starts directly after the BSS 
+		 * our free area starts directly after the BSS
 		 */
 		memory_start = (unsigned long) &_end;
 	} else {
 		/* otherwise the free area starts after the ROM filesystem */
-		printk("ROM fs in RAM, size %d bytes\n", romfs_length);
+		printk("ROM fs in RAM, size %lu bytes\n", romfs_length);
 		memory_start = romfs_start + romfs_length;
 	}
 
@@ -193,7 +195,7 @@
 #define HAS_ATA		0x0020
 #define HAS_USB		0x0040
 #define HAS_IRQ_BUG	0x0080
-#define HAS_MMU_BUG     0x0100
+#define HAS_MMU_BUG	0x0100
 
 static struct cpu_info {
 	char *model;
@@ -213,50 +215,27 @@
 	{ "ETRAX 100",       8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_IRQ_BUG },
 	{ "ETRAX 100",       8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA },
 	{ "ETRAX 100LX",     8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB | HAS_MMU | HAS_MMU_BUG },
-	{ "ETRAX 100LX v2",  8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB | HAS_MMU },
+	{ "ETRAX 100LX v2",  8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB | HAS_MMU  },
 	{ "Unknown",         0, 0 }  /* This entry MUST be the last */
 };
 
-/*
- * get_cpuinfo - Get information on one CPU for use by the procfs.
- *
- *	Prints info on the next CPU into buffer.  Beware, doesn't check for
- *	buffer overflow.  Current implementation of procfs assumes that the
- *	resulting data is <= 1K.
- *
- *	BUFFER is PAGE_SIZE - 1K bytes long.
- *
- * Args:
- *	buffer	-- you guessed it, the data buffer
- *	cpu_np	-- Input: next cpu to get (start at 0).  Output: Updated.
- *
- *	Returns number of bytes written to buffer.
- */
-int get_cpuinfo(char *buffer, unsigned *cpu_np)
+static int show_cpuinfo(struct seq_file *m, void *v)
 {
-	int revision;
- 	struct cpu_info *info;
-	unsigned n;
+	unsigned long revision;
+	struct cpu_info *info;
 
 	/* read the version register in the CPU and print some stuff */
 
 	revision = rdvr();
 
-	if (revision < 0 || revision >= sizeof cpu_info/sizeof *cpu_info) {
+	if (revision >= sizeof cpu_info/sizeof *cpu_info)
 		info = &cpu_info[sizeof cpu_info/sizeof *cpu_info - 1];
-	} else
+	else
 		info = &cpu_info[revision];
 
-  	/* No SMP at the moment, so just toggle 0/1 */
-	n = *cpu_np;
-	*cpu_np = 1;
-	if (n != 0) {
-		return (0);
-	}
-
-	return sprintf(buffer,
+	return seq_printf(m,
 		       "cpu\t\t: CRIS\n"
-		       "cpu revision\t: %d\n"
+		       "cpu revision\t: %lu\n"
 		       "cpu model\t: %s\n"
 		       "cache size\t: %d kB\n"
 		       "fpu\t\t: %s\n"
@@ -283,4 +262,28 @@
 		       (loops_per_jiffy * HZ + 500) / 500000,
 		       ((loops_per_jiffy * HZ + 500) / 5000) % 100);
 }
+
+static void *c_start(struct seq_file *m, loff_t *pos)
+{
+	/* We only got one CPU... */
+	return *pos < 1 ? (void *)1 : NULL;
+}
+
+static void *c_next(struct seq_file *m, void *v, loff_t *pos)
+{
+	++*pos;
+	return NULL;
+}
+
+static void c_stop(struct seq_file *m, void *v)
+{
+}
+
+struct seq_operations cpuinfo_op = {
+	start:  c_start,
+	next:   c_next,
+	stop:   c_stop,
+	show:   show_cpuinfo,
+};
+
 #endif /* CONFIG_PROC_FS */
diff -urN linux-2.4.18-pre2/arch/cris/kernel/time.c linux/arch/cris/kernel/time.c
--- linux-2.4.18-pre2/arch/cris/kernel/time.c	Fri Nov  9 13:58:02 2001
+++ linux/arch/cris/kernel/time.c	Thu Jan 10 13:28:32 2002
@@ -1,4 +1,4 @@
-/* $Id: time.c,v 1.9 2001/10/25 10:26:37 johana Exp $
+/* $Id: time.c,v 1.11 2001/11/12 18:26:22 pkj Exp $
  *
  *  linux/arch/cris/kernel/time.c
  *
@@ -18,6 +18,7 @@
  * Linux/CRIS specific code:
  *
  * Authors:    Bjorn Wesen
+ *             Johan Adolfsson  
  *
  */
 
@@ -61,6 +62,7 @@
 static unsigned long do_slow_gettimeoffset(void)
 {
 	unsigned long count;
+	unsigned long usec_count = 0;
 
 	static unsigned long count_p = LATCH;    /* for the first call after boot */
 	static unsigned long jiffies_p = 0;
@@ -93,16 +95,20 @@
  */
 	if( jiffies_t == jiffies_p ) {
 		if( count > count_p ) {
+			/* Timer wrapped */
+			count = count_p;
+			usec_count = 1000000/CLOCK_TICK_RATE/2;
 		}
 	} else
 		jiffies_p = jiffies_t;
-
         count_p = count;
-
+	/* Convert timer value to usec using table lookup */
+	usec_count += cris_timer0_value_us[count];
+#if 0
 	count = ((LATCH-1) - count) * TICK_SIZE;
 	count = (count + LATCH/2) / LATCH;
-
-	return count;
+#endif
+	return usec_count;
 }
 
 static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset;
@@ -160,9 +166,8 @@
 {
 	int retval = 0;
 	int real_seconds, real_minutes, cmos_minutes;
-	unsigned char save_control, save_freq_select;
 
-	printk("set_rtc_mmss(%d)\n", nowtime);
+	printk("set_rtc_mmss(%lu)\n", nowtime);
 
 	if(!have_rtc)
 		return 0;
@@ -225,7 +230,9 @@
 /* right now, starting the watchdog is the same as resetting it */
 #define start_watchdog reset_watchdog
 
+#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM)
 static int watchdog_key = 0;  /* arbitrary number */
+#endif
 
 /* number of pages to consider "out of memory". it is normal that the memory
  * is used though, so put this really low.
@@ -306,12 +313,12 @@
 	if ((time_status & STA_UNSYNC) == 0 &&
 	    xtime.tv_sec > last_rtc_update + 660 &&
 	    xtime.tv_usec > 500000 - (tick >> 1) &&
-	    xtime.tv_usec < 500000 + (tick >> 1))
+	    xtime.tv_usec < 500000 + (tick >> 1)) {
 		if (set_rtc_mmss(xtime.tv_sec) == 0)
 			last_rtc_update = xtime.tv_sec;
 		else
 			last_rtc_update = xtime.tv_sec - 600;
-	
+	}
 }
 
 #if 0
@@ -322,6 +329,7 @@
 {
 	unsigned long flags;
 	unsigned int newjiff;
+
 	save_flags(flags);
 	cli();
 	newjiff = (myjiff << 16) | (unsigned short)(-*R_TIMER01_DATA); 
@@ -337,7 +345,6 @@
 get_cmos_time(void)
 {
 	unsigned int year, mon, day, hour, min, sec;
-	int i;
 
 	sec = CMOS_READ(RTC_SECONDS);
 	min = CMOS_READ(RTC_MINUTES);
diff -urN linux-2.4.18-pre2/arch/cris/kernel/traps.c linux/arch/cris/kernel/traps.c
--- linux-2.4.18-pre2/arch/cris/kernel/traps.c	Fri Nov  9 13:58:02 2001
+++ linux/arch/cris/kernel/traps.c	Thu Jan 10 13:28:32 2002
@@ -1,4 +1,4 @@
-/* $Id: traps.c,v 1.15 2001/07/18 14:02:37 bjornw Exp $
+/* $Id: traps.c,v 1.17 2001/12/07 17:02:34 bjornw Exp $
  *
  *  linux/arch/cris/traps.c
  *
@@ -30,6 +30,55 @@
 
 int kstack_depth_to_print = 24;
 
+void show_trace(unsigned long * stack)
+{
+	unsigned long addr, module_start, module_end;
+	extern char _stext, _etext;
+	int i;
+
+        printk("\nCall Trace: ");
+
+        i = 1;
+        module_start = VMALLOC_START;
+        module_end = VMALLOC_END;
+
+        while (((long) stack & (THREAD_SIZE-1)) != 0) {
+		if (__get_user (addr, stack)) {
+			/* This message matches "failing address" marked
+			   s390 in ksymoops, so lines containing it will
+			   not be filtered out by ksymoops.  */
+			printk ("Failing address 0x%lx\n", (unsigned long)stack);
+			break;
+		}
+		stack++;
+
+                /*
+                 * If the address is either in the text segment of the
+                 * kernel, or in the region which contains vmalloc'ed
+                 * memory, it *may* be the address of a calling
+                 * routine; if so, print it so that someone tracing
+                 * down the cause of the crash will be able to figure
+                 * out the call path that was taken.
+                 */
+                if (((addr >= (unsigned long) &_stext) &&
+                     (addr <= (unsigned long) &_etext)) ||
+                    ((addr >= module_start) && (addr <= module_end))) {
+                        if (i && ((i % 8) == 0))
+                                printk("\n       ");
+                        printk("[<%08lx>] ", addr);
+                        i++;
+                }
+        }
+}
+
+void show_trace_task(struct task_struct *tsk)
+{
+	/* TODO, this is not really useful since its called from
+	 * SysRq-T and we don't have a keyboard.. :) 
+	 */
+}
+
+
 /*
  * These constants are for searching for possible module text
  * segments. MODULE_RANGE is a guess of how much space is likely
@@ -48,9 +97,8 @@
 void 
 show_stack(unsigned long *sp)
 {
-        unsigned long *stack, addr, module_start, module_end;
+        unsigned long *stack, addr;
         int i;
-	extern char _stext, _etext;
 
 	/*
 	 * debugging aid: "show_stack(NULL);" prints a
@@ -62,7 +110,7 @@
 
         stack = sp;
 
-	printk("\nStack from %08lx:\n       ", stack);
+	printk("\nStack from %08lx:\n       ", (unsigned long)stack);
         for(i = 0; i < kstack_depth_to_print; i++) {
                 if (((long) stack & (THREAD_SIZE-1)) == 0)
                         break;
@@ -72,45 +120,13 @@
 			/* This message matches "failing address" marked
 			   s390 in ksymoops, so lines containing it will
 			   not be filtered out by ksymoops.  */
-			printk ("Failing address 0x%lx\n", stack);
+			printk ("Failing address 0x%lx\n", (unsigned long)stack);
 			break;
 		}
 		stack++;
 		printk("%08lx ", addr);
         }
-
-        printk("\nCall Trace: ");
-        stack = sp;
-        i = 1;
-        module_start = VMALLOC_START;
-        module_end = VMALLOC_END;
-        while (((long) stack & (THREAD_SIZE-1)) != 0) {
-		if (__get_user (addr, stack)) {
-			/* This message matches "failing address" marked
-			   s390 in ksymoops, so lines containing it will
-			   not be filtered out by ksymoops.  */
-			printk ("Failing address 0x%lx\n", stack);
-			break;
-		}
-		stack++;
-
-                /*
-                 * If the address is either in the text segment of the
-                 * kernel, or in the region which contains vmalloc'ed
-                 * memory, it *may* be the address of a calling
-                 * routine; if so, print it so that someone tracing
-                 * down the cause of the crash will be able to figure
-                 * out the call path that was taken.
-                 */
-                if (((addr >= (unsigned long) &_stext) &&
-                     (addr <= (unsigned long) &_etext)) ||
-                    ((addr >= module_start) && (addr <= module_end))) {
-                        if (i && ((i % 8) == 0))
-                                printk("\n       ");
-                        printk("[<%08lx>] ", addr);
-                        i++;
-                }
-        }
+	show_trace(sp);
 }
 
 #if 0
@@ -148,7 +164,7 @@
 	       regs->r8, regs->r9, regs->r10, regs->r11);
 	printk("r12: %08lx r13: %08lx oR10: %08lx\n",
 	       regs->r12, regs->r13, regs->orig_r10);
-	printk("R_MMU_CAUSE: %08lx\n", *R_MMU_CAUSE);
+	printk("R_MMU_CAUSE: %08lx\n", (unsigned long)*R_MMU_CAUSE);
 	printk("Process %s (pid: %d, stackpage=%08lx)\n",
 	       current->comm, current->pid, (unsigned long)current);
 
@@ -195,25 +211,56 @@
         }
 }
 
+/* Called from entry.S when the watchdog has bitten
+ * We print out something resembling an oops dump, and if
+ * we have the nice doggy development flag set, we halt here
+ * instead of rebooting.
+ */
+
+void
+watchdog_bite_hook(struct pt_regs *regs)
+{
+#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
+	cli();
+	stop_watchdog();
+	show_registers(regs);
+	while(1) /* nothing */;
+#else
+	show_registers(regs);
+#endif	
+}
+
+/* This is normally the 'Oops' routine */
+
 void 
 die_if_kernel(const char * str, struct pt_regs * regs, long err)
 {
+	extern void reset_watchdog(void);
+	extern void stop_watchdog(void);
+
 	if(user_mode(regs))
 		return;
 
+#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
+	/* This printout might take too long and trigger the 
+	 * watchdog normally. If we're in the nice doggy
+	 * development mode, stop the watchdog during printout.
+	 */
 	stop_watchdog();
+#endif
 
 	printk("%s: %04lx\n", str, err & 0xffff);
 
 	show_registers(regs);
 
+#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
 	reset_watchdog();
-
+#endif
 	do_exit(SIGSEGV);
 }
 
 void __init 
 trap_init(void)
 {
-  /* Nothing needs to be done */
+	/* Nothing needs to be done */
 }
diff -urN linux-2.4.18-pre2/arch/cris/mm/fault.c linux/arch/cris/mm/fault.c
--- linux-2.4.18-pre2/arch/cris/mm/fault.c	Thu Jul 26 15:10:06 2001
+++ linux/arch/cris/mm/fault.c	Thu Jan 10 13:28:32 2002
@@ -6,6 +6,17 @@
  *  Authors:  Bjorn Wesen 
  * 
  *  $Log: fault.c,v $
+ *  Revision 1.20  2001/11/22 13:34:06  bjornw
+ *  * Bug workaround (LX TR89): force a rerun of the whole of an interrupted
+ *    unaligned write, because the second half of the write will be corrupted
+ *    otherwise. Affected unaligned writes spanning not-yet mapped pages.
+ *  * Optimization: use the wr_rd bit in R_MMU_CAUSE to know whether a miss
+ *    was due to a read or a write (before we didn't know this until the next
+ *    restart of the interrupted instruction, thus wasting one fault-irq)
+ *
+ *  Revision 1.19  2001/11/12 19:02:10  pkj
+ *  Fixed compiler warnings.
+ *
  *  Revision 1.18  2001/07/18 22:14:32  bjornw
  *  Enable interrupts in the bulk of do_page_fault
  *
@@ -78,7 +89,14 @@
 			      int error_code);
 
 /* debug of low-level TLB reload */
+#undef DEBUG
+
+#ifdef DEBUG
+#define D(x) x
+#else
 #define D(x)
+#endif
+
 /* debug of higher-level faults */
 #define DPG(x)
 
@@ -94,9 +112,12 @@
 handle_mmu_bus_fault(struct pt_regs *regs)
 {
 	int cause, select;
+#ifdef DEBUG
 	int index;
 	int page_id;
-	int miss, we, acc, inv;  
+	int acc, inv;
+#endif
+	int miss, we, writeac;
 	pmd_t *pmd;
 	pte_t pte;
 	int errcode;
@@ -106,75 +127,83 @@
 	select = *R_TLB_SELECT;
 
 	address = cause & PAGE_MASK; /* get faulting address */
-	
-	D(page_id = IO_EXTRACT(R_MMU_CAUSE,  page_id,   cause));
-	D(acc     = IO_EXTRACT(R_MMU_CAUSE,  acc_excp,  cause));
-	D(inv     = IO_EXTRACT(R_MMU_CAUSE,  inv_excp,  cause));  
-	D(index  =  IO_EXTRACT(R_TLB_SELECT, index,     select));
+
+#ifdef DEBUG
+	page_id = IO_EXTRACT(R_MMU_CAUSE,  page_id,   cause);
+	acc     = IO_EXTRACT(R_MMU_CAUSE,  acc_excp,  cause);
+	inv     = IO_EXTRACT(R_MMU_CAUSE,  inv_excp,  cause);  
+	index   = IO_EXTRACT(R_TLB_SELECT, index,     select);
+#endif
 	miss    = IO_EXTRACT(R_MMU_CAUSE,  miss_excp, cause);
 	we      = IO_EXTRACT(R_MMU_CAUSE,  we_excp,   cause);
+	writeac = IO_EXTRACT(R_MMU_CAUSE,  wr_rd,     cause);
+
+	/* ETRAX 100LX TR89 bugfix: if the second half of an unaligned
+	 * write causes a MMU-fault, it will not be restarted correctly.
+	 * This could happen if a write crosses a page-boundary and the
+	 * second page is not yet COW'ed or even loaded. The workaround
+	 * is to clear the unaligned bit in the CPU status record, so 
+	 * that the CPU will rerun both the first and second halves of
+	 * the instruction. This will not have any sideeffects unless
+	 * the first half goes to any device or memory that can't be
+	 * written twice, and which is mapped through the MMU.
+	 *
+	 * We only need to do this for writes.
+	 */
+
+	if(writeac)
+		regs->csrinstr &= ~(1 << 5);
 	
-	/* Note: the reason we don't set errcode's r/w flag here
-	 * using the 'we' flag, is because the latter is only given
-	 * if there is a write-protection exception, not given as a
-	 * general r/w access mode flag. It is currently not possible
-	 * to get this from the MMU (TODO: check if this is the case
-	 * for LXv2).
-	 * 
-	 * The page-fault code won't care, but there will be two page-
-	 * faults instead of one for the case of a write to a non-tabled
-	 * page (miss, then write-protection).
+	/* Set errcode's R/W flag according to the mode which caused the
+	 * fault
 	 */
 
-	errcode = 0;
+	errcode = writeac << 1;
 
-	D(printk("bus_fault from IRP 0x%x: addr 0x%x, miss %d, inv %d, we %d, acc %d, "
-		 "idx %d pid %d\n",
+	D(printk("bus_fault from IRP 0x%lx: addr 0x%lx, miss %d, inv %d, we %d, acc %d, dx %d pid %d\n",
 		 regs->irp, address, miss, inv, we, acc, index, page_id));
 
 	/* for a miss, we need to reload the TLB entry */
 
-	if(miss) {
-
+	if (miss) {
 		/* see if the pte exists at all
 		 * refer through current_pgd, dont use mm->pgd
 		 */
-		
+
 		pmd = (pmd_t *)(current_pgd + pgd_index(address));
-		if(pmd_none(*pmd))
+		if (pmd_none(*pmd))
 			goto dofault;
-		if(pmd_bad(*pmd)) {
-			printk("bad pgdir entry 0x%x at 0x%x\n", *pmd, pmd);
+		if (pmd_bad(*pmd)) {
+			printk("bad pgdir entry 0x%lx at 0x%p\n", *(unsigned long*)pmd, pmd);
 			pmd_clear(pmd);
 			return;
 		}
 		pte = *pte_offset(pmd, address);
-		if(!pte_present(pte))
+		if (!pte_present(pte))
 			goto dofault;
-		
-		D(printk(" found pte %x pg %x ", pte_val(pte), pte_page(pte)));
-		D(
-		  {
-			  if(pte_val(pte) & _PAGE_SILENT_WRITE)
-				  printk("Silent-W ");
-			  if(pte_val(pte) & _PAGE_KERNEL)
-				  printk("Kernel ");
-			  if(pte_val(pte) & _PAGE_SILENT_READ)
-				  printk("Silent-R ");
-			  if(pte_val(pte) & _PAGE_GLOBAL)
-				  printk("Global ");
-			  if(pte_val(pte) & _PAGE_PRESENT)
-				  printk("Present ");
-			  if(pte_val(pte) & _PAGE_ACCESSED)
-				  printk("Accessed ");
-			  if(pte_val(pte) & _PAGE_MODIFIED)
-				  printk("Modified ");
-			  if(pte_val(pte) & _PAGE_READ)
-				  printk("Readable ");
-			  if(pte_val(pte) & _PAGE_WRITE)
-				  printk("Writeable ");
-			  printk("\n");
-		  });
+
+#ifdef DEBUG
+		printk(" found pte %lx pg %p ", pte_val(pte), pte_page(pte));
+		if (pte_val(pte) & _PAGE_SILENT_WRITE)
+			printk("Silent-W ");
+		if (pte_val(pte) & _PAGE_KERNEL)
+			printk("Kernel ");
+		if (pte_val(pte) & _PAGE_SILENT_READ)
+			printk("Silent-R ");
+		if (pte_val(pte) & _PAGE_GLOBAL)
+			printk("Global ");
+		if (pte_val(pte) & _PAGE_PRESENT)
+			printk("Present ");
+		if (pte_val(pte) & _PAGE_ACCESSED)
+			printk("Accessed ");
+		if (pte_val(pte) & _PAGE_MODIFIED)
+			printk("Modified ");
+		if (pte_val(pte) & _PAGE_READ)
+			printk("Readable ");
+		if (pte_val(pte) & _PAGE_WRITE)
+			printk("Writeable ");
+		printk("\n");
+#endif
 
 		/* load up the chosen TLB entry
 		 * this assumes the pte format is the same as the TLB_LO layout.
@@ -189,10 +218,10 @@
 	} 
 
 	errcode = 1 | (we << 1);
-	
+
  dofault:
 	/* leave it to the MM system fault handler below */
-	D(printk("do_page_fault %p errcode %d\n", address, errcode));
+	D(printk("do_page_fault %lx errcode %d\n", address, errcode));
 	do_page_fault(address, regs, errcode);
 }
 
@@ -221,20 +250,19 @@
 	struct mm_struct *mm;
 	struct vm_area_struct * vma;
 	int writeaccess;
-	int fault;
 	unsigned long fixup;
 	siginfo_t info;
 
 	tsk = current;
 
-        /*
-         * We fault-in kernel-space virtual memory on-demand. The
-         * 'reference' page table is init_mm.pgd.
-         *
-         * NOTE! We MUST NOT take any locks for this case. We may
-         * be in an interrupt or a critical region, and should
-         * only copy the information from the master page table,
-         * nothing more.
+	/*
+	 * We fault-in kernel-space virtual memory on-demand. The
+	 * 'reference' page table is init_mm.pgd.
+	 *
+	 * NOTE! We MUST NOT take any locks for this case. We may
+	 * be in an interrupt or a critical region, and should
+	 * only copy the information from the master page table,
+	 * nothing more.
 	 *
 	 * NOTE2: This is done so that, when updating the vmalloc
 	 * mappings we don't have to walk all processes pgdirs and
@@ -243,13 +271,13 @@
 	 * bit set so sometimes the TLB can use a lingering entry.
 	 *
 	 * This verifies that the fault happens in kernel space
-         * and that the fault was not a protection error (error_code & 1).
-         */
+	 * and that the fault was not a protection error (error_code & 1).
+	 */
 
-        if (address >= VMALLOC_START &&
+	if (address >= VMALLOC_START &&
 	    !(error_code & 1) &&
 	    !user_mode(regs))
-                goto vmalloc_fault;
+		goto vmalloc_fault;
 
 	/* we can and should enable interrupts at this point */
 	sti();
@@ -312,28 +340,27 @@
 	 */
 
 	switch (handle_mm_fault(mm, vma, address, writeaccess)) {
-        case 1:
-                tsk->min_flt++;
-                break;
-        case 2:
-                tsk->maj_flt++;
-                break;
-        case 0:
-                goto do_sigbus;
-        default:
-                goto out_of_memory;
+	case 1:
+		tsk->min_flt++;
+		break;
+	case 2:
+		tsk->maj_flt++;
+		break;
+	case 0:
+		goto do_sigbus;
+	default:
+		goto out_of_memory;
 	}
 
 	up_read(&mm->mmap_sem);
 	return;
-	
+
 	/*
 	 * Something tried to access memory that isn't in our memory map..
 	 * Fix it, but check if it's kernel or user first..
 	 */
 
  bad_area:
-
 	up_read(&mm->mmap_sem);
 
  bad_area_nosemaphore:
@@ -361,10 +388,10 @@
 	 *  code)
 	 */
 
-        if ((fixup = search_exception_table(regs->irp)) != 0) {
+	if ((fixup = search_exception_table(regs->irp)) != 0) {
 		/* Adjust the instruction pointer in the stackframe */
 
-                regs->irp = fixup;
+		regs->irp = fixup;
 
 		/* We do not want to return by restoring the CPU-state
 		 * anymore, so switch frame-types (see ptrace.h)
@@ -372,9 +399,9 @@
 
 		regs->frametype = CRIS_FRAME_NORMAL;
 
-		D(printk("doing fixup to 0x%x\n", fixup));
-                return;
-        }
+		D(printk("doing fixup to 0x%lx\n", fixup));
+		return;
+	}
 
 	/*
 	 * Oops. The kernel tried to access some bad page. We'll have to
@@ -397,9 +424,9 @@
 	 */
 
  out_of_memory:
-        up_read(&mm->mmap_sem);
+	up_read(&mm->mmap_sem);
 	printk("VM: killing process %s\n", tsk->comm);
-	if(user_mode(regs))
+	if (user_mode(regs))
 		do_exit(SIGKILL);
 	goto no_context;
 
@@ -407,40 +434,40 @@
 	up_read(&mm->mmap_sem);
 
 	/*
-         * Send a sigbus, regardless of whether we were in kernel
-         * or user mode.
-         */
+	 * Send a sigbus, regardless of whether we were in kernel
+	 * or user mode.
+	 */
 	info.si_code = SIGBUS;
 	info.si_errno = 0;
 	info.si_code = BUS_ADRERR;
 	info.si_addr = (void *)address;
 	force_sig_info(SIGBUS, &info, tsk);
-	
-        /* Kernel mode? Handle exceptions or die */
-        if (!user_mode(regs))
-                goto no_context;
-        return;
+
+	/* Kernel mode? Handle exceptions or die */
+	if (!user_mode(regs))
+		goto no_context;
+	return;
 
 vmalloc_fault:
-        {
-                /*
-                 * Synchronize this task's top level page-table
-                 * with the 'reference' page table.
+	{
+		/*
+		 * Synchronize this task's top level page-table
+		 * with the 'reference' page table.
 		 *
 		 * Use current_pgd instead of tsk->active_mm->pgd
 		 * since the latter might be unavailable if this
 		 * code is executed in a misfortunately run irq
 		 * (like inside schedule() between switch_mm and
 		 *  switch_to...).
-                 */
+		 */
 
-                int offset = pgd_index(address);
-                pgd_t *pgd, *pgd_k;
-                pmd_t *pmd, *pmd_k;
+		int offset = pgd_index(address);
+		pgd_t *pgd, *pgd_k;
+		pmd_t *pmd, *pmd_k;
 		pte_t *pte_k;
 
-                pgd = current_pgd + offset;
-                pgd_k = init_mm.pgd + offset;
+		pgd = (pgd_t *)current_pgd + offset;
+		pgd_k = init_mm.pgd + offset;
 
 		/* Since we're two-level, we don't need to do both
 		 * set_pgd and set_pmd (they do the same thing). If
@@ -454,13 +481,13 @@
 		 * it exists.
 		 */
 
-                pmd = pmd_offset(pgd, address);
-                pmd_k = pmd_offset(pgd_k, address);
+		pmd = pmd_offset(pgd, address);
+		pmd_k = pmd_offset(pgd_k, address);
 
-                if (!pmd_present(*pmd_k))
-                        goto bad_area_nosemaphore;
+		if (!pmd_present(*pmd_k))
+			goto bad_area_nosemaphore;
 
-                set_pmd(pmd, *pmd_k);
+		set_pmd(pmd, *pmd_k);
 
 		/* Make sure the actual PTE exists as well to
 		 * catch kernel vmalloc-area accesses to non-mapped
@@ -468,10 +495,10 @@
 		 * silently loop forever.
 		 */
 
-                pte_k = pte_offset(pmd_k, address);
-                if (!pte_present(*pte_k))
-                        goto no_context;
+		pte_k = pte_offset(pmd_k, address);
+		if (!pte_present(*pte_k))
+			goto no_context;
 
-                return;
-        }
+		return;
+	}
 }
diff -urN linux-2.4.18-pre2/arch/cris/mm/init.c linux/arch/cris/mm/init.c
--- linux-2.4.18-pre2/arch/cris/mm/init.c	Thu Jul 26 15:10:06 2001
+++ linux/arch/cris/mm/init.c	Thu Jan 10 13:28:32 2002
@@ -7,6 +7,12 @@
  *  Authors:  Bjorn Wesen (bjornw@axis.com)
  *
  *  $Log: init.c,v $
+ *  Revision 1.31  2001/11/13 16:22:00  bjornw
+ *  Skip calculating totalram and sharedram in si_meminfo
+ *
+ *  Revision 1.30  2001/11/12 19:02:10  pkj
+ *  Fixed compiler warnings.
+ *
  *  Revision 1.29  2001/07/25 16:09:50  bjornw
  *  val->sharedram will stay 0
  *
@@ -459,29 +465,18 @@
                 free_page(addr);
                 totalram_pages++;
         }
-        printk ("Freeing unused kernel memory: %dk freed\n", 
+        printk ("Freeing unused kernel memory: %luk freed\n", 
 		(&__init_end - &__init_begin) >> 10);
 }
 
 void 
 si_meminfo(struct sysinfo *val)
 {
-	int i;
-
-	i = max_mapnr;
-	val->totalram = 0;
-	val->sharedram = 0;
-	val->freeram = nr_free_pages();
-	val->bufferram = atomic_read(&buffermem_pages);
-	while (i-- > 0)  {
-		if (PageReserved(mem_map+i))
-			continue;
-		val->totalram++;
-		if (!atomic_read(&mem_map[i].count))
-			continue;
-		val->sharedram += atomic_read(&mem_map[i].count) - 1;
-	}
-	val->mem_unit = PAGE_SIZE;
-	val->totalhigh = 0;
-	val->freehigh = 0;
+        val->totalram = totalram_pages;
+        val->sharedram = 0;
+        val->freeram = nr_free_pages();
+        val->bufferram = atomic_read(&buffermem_pages);
+        val->totalhigh = 0;
+        val->freehigh = 0;
+        val->mem_unit = PAGE_SIZE;
 }
diff -urN linux-2.4.18-pre2/arch/i386/boot/compressed/Makefile linux/arch/i386/boot/compressed/Makefile
--- linux-2.4.18-pre2/arch/i386/boot/compressed/Makefile	Tue Sep 11 23:08:12 2001
+++ linux/arch/i386/boot/compressed/Makefile	Thu Jan 10 13:28:32 2002
@@ -32,8 +32,10 @@
 head.o: head.S
 	$(CC) $(AFLAGS) -traditional -c head.S
 
+comma	:= ,
+
 misc.o: misc.c
-	$(CC) $(CFLAGS) -c misc.c
+	$(CC) $(CFLAGS) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) -c misc.c
 
 piggy.o:	$(SYSTEM)
 	tmppiggy=_tmp_$$$$piggy; \
diff -urN linux-2.4.18-pre2/arch/i386/defconfig linux/arch/i386/defconfig
--- linux-2.4.18-pre2/arch/i386/defconfig	Mon Nov 12 11:59:03 2001
+++ linux/arch/i386/defconfig	Thu Jan 10 13:28:32 2002
@@ -496,6 +496,9 @@
 # IrDA (infrared) support
 #
 # CONFIG_IRDA is not set
+CONFIG_IRDA_CACHE_LAST_LSAP=y
+CONFIG_IRDA_FAST_RR=y
+CONFIG_IRDA_DEBUG=y
 
 #
 # ISDN subsystem
diff -urN linux-2.4.18-pre2/arch/i386/kernel/dmi_scan.c linux/arch/i386/kernel/dmi_scan.c
--- linux-2.4.18-pre2/arch/i386/kernel/dmi_scan.c	Fri Dec 21 09:41:53 2001
+++ linux/arch/i386/kernel/dmi_scan.c	Thu Jan 10 13:28:32 2002
@@ -467,6 +467,11 @@
 			MATCH(DMI_PRODUCT_NAME, "PowerEdge 300/"),
 			NO_MATCH, NO_MATCH
 			} },
+	{ set_bios_reboot, "Dell PowerEdge 2400", {  /* Handle problems with rebooting on Dell 300/800's */
+			MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
+			MATCH(DMI_PRODUCT_NAME, "PowerEdge 2400"),
+			NO_MATCH, NO_MATCH
+			} },
 	{ set_apm_ints, "Dell Inspiron", {	/* Allow interrupts during suspend on Dell Inspiron laptops*/
 			MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
 			MATCH(DMI_PRODUCT_NAME, "Inspiron 4000"),
diff -urN linux-2.4.18-pre2/arch/i386/kernel/head.S linux/arch/i386/kernel/head.S
--- linux-2.4.18-pre2/arch/i386/kernel/head.S	Wed Jun 20 11:00:53 2001
+++ linux/arch/i386/kernel/head.S	Thu Jan 10 13:28:32 2002
@@ -446,12 +446,3 @@
 	.quad 0x00009a0000000000	/* 0x50 APM CS 16 code (16 bit) */
 	.quad 0x0040920000000000	/* 0x58 APM DS    data */
 	.fill NR_CPUS*4,8,0		/* space for TSS's and LDT's */
-		
-/*
- * This is to aid debugging, the various locking macros will be putting
- * code fragments here.  When an oops occurs we'd rather know that it's
- * inside the .text.lock section rather than as some offset from whatever
- * function happens to be last in the .text segment.
- */
-.section .text.lock
-ENTRY(stext_lock)
diff -urN linux-2.4.18-pre2/arch/i386/vmlinux.lds linux/arch/i386/vmlinux.lds
--- linux-2.4.18-pre2/arch/i386/vmlinux.lds	Mon Jul  2 14:40:14 2001
+++ linux/arch/i386/vmlinux.lds	Thu Jan 10 13:28:32 2002
@@ -13,7 +13,6 @@
 	*(.fixup)
 	*(.gnu.warning)
 	} = 0x9090
-  .text.lock : { *(.text.lock) }	/* out-of-line lock text */
 
   _etext = .;			/* End of text section */
 
diff -urN linux-2.4.18-pre2/arch/ia64/sn/fprom/Makefile linux/arch/ia64/sn/fprom/Makefile
--- linux-2.4.18-pre2/arch/ia64/sn/fprom/Makefile	Thu Apr  5 12:51:47 2001
+++ linux/arch/ia64/sn/fprom/Makefile	Thu Jan 10 13:28:32 2002
@@ -18,10 +18,12 @@
 fprom: $(OBJ)
 	$(LD) -static -Tfprom.lds -o fprom $(OBJ) $(LIB)
 
+comma	:= ,
+
 .S.o:
 	$(CC)  -D__ASSEMBLY__ $(AFLAGS) $(AFLAGS_KERNEL) -c -o $*.o $<
 .c.o:
-	$(CC)  $(CFLAGS) $(CFLAGS_KERNEL) -c -o $*.o $<
+	$(CC)  $(CFLAGS) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) $(CFLAGS_KERNEL) -c -o $*.o $<
 
 clean:
 	rm -f *.o fprom
diff -urN linux-2.4.18-pre2/arch/ia64/tools/Makefile linux/arch/ia64/tools/Makefile
--- linux-2.4.18-pre2/arch/ia64/tools/Makefile	Mon Oct  9 17:54:57 2000
+++ linux/arch/ia64/tools/Makefile	Thu Jan 10 13:28:32 2002
@@ -31,8 +31,10 @@
 offsets.h: print_offsets
 	./print_offsets > offsets.h
 
+comma	:= ,
+
 print_offsets: print_offsets.c FORCE_RECOMPILE
-	$(CC) $(CFLAGS) print_offsets.c -o $@
+	$(CC) $(CFLAGS) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) print_offsets.c -o $@
 
 FORCE_RECOMPILE:
 
@@ -42,7 +44,7 @@
 	$(AWK) -f print_offsets.awk $^ > $@
 
 print_offsets.s: print_offsets.c
-	$(CC) $(CFLAGS) -S print_offsets.c -o $@
+	$(CC) $(CFLAGS) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) -S print_offsets.c -o $@
 
 endif
 
diff -urN linux-2.4.18-pre2/arch/m68k/vmlinux-sun3.lds linux/arch/m68k/vmlinux-sun3.lds
--- linux-2.4.18-pre2/arch/m68k/vmlinux-sun3.lds	Mon Jul  2 14:40:14 2001
+++ linux/arch/m68k/vmlinux-sun3.lds	Thu Jan 10 13:28:32 2002
@@ -10,7 +10,6 @@
 	*(.head)
 	*(.text)
 	*(.fixup)
-	*(.text.lock)		/* out-of-line lock text */
 	*(.gnu.warning)
 	} = 0x4e75
   .kstrtab : { *(.kstrtab) }
diff -urN linux-2.4.18-pre2/arch/m68k/vmlinux.lds linux/arch/m68k/vmlinux.lds
--- linux-2.4.18-pre2/arch/m68k/vmlinux.lds	Mon Jul  2 14:40:14 2001
+++ linux/arch/m68k/vmlinux.lds	Thu Jan 10 13:28:32 2002
@@ -9,7 +9,6 @@
   .text : {
 	*(.text)
 	*(.fixup)
-	*(.text.lock)		/* out-of-line lock text */
 	*(.gnu.warning)
 	} = 0x4e75
   .rodata : { *(.rodata) *(.rodata.*) }
diff -urN linux-2.4.18-pre2/arch/ppc/8260_io/uart.c linux/arch/ppc/8260_io/uart.c
--- linux-2.4.18-pre2/arch/ppc/8260_io/uart.c	Thu Jan 10 13:28:30 2002
+++ linux/arch/ppc/8260_io/uart.c	Thu Jan 10 13:28:32 2002
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.uart.c 1.11 12/13/01 16:53:53 trini
+ * BK Id: SCCS/s.uart.c 1.13 12/29/01 14:50:03 trini
  */
 /*
  *  UART driver for MPC8260 CPM SCC or SMC
@@ -1736,7 +1736,7 @@
 		schedule_timeout(char_time);
 		if (signal_pending(current))
 			break;
-		if (timeout && ((orig_jiffies + timeout) < jiffies))
+		if (timeout && time_after(jiffies, orig_jiffies + timeout))
 			break;
 		bdp = info->tx_cur;
 	} while (bdp->cbd_sc & BD_SC_READY);
diff -urN linux-2.4.18-pre2/arch/ppc/8xx_io/uart.c linux/arch/ppc/8xx_io/uart.c
--- linux-2.4.18-pre2/arch/ppc/8xx_io/uart.c	Thu Jan 10 13:28:30 2002
+++ linux/arch/ppc/8xx_io/uart.c	Thu Jan 10 13:28:32 2002
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.uart.c 1.21 12/13/01 16:53:53 trini
+ * BK Id: SCCS/s.uart.c 1.23 12/29/01 14:50:03 trini
  */
 /*
  *  UART driver for MPC860 CPM SCC or SMC
@@ -1802,7 +1802,7 @@
 		schedule_timeout(char_time);
 		if (signal_pending(current))
 			break;
-		if (timeout && ((orig_jiffies + timeout) < jiffies))
+		if (timeout && time_after(jiffies, orig_jiffies + timeout))
 			break;
 
 		/* The 'tx_cur' is really the next buffer to send.  We
diff -urN linux-2.4.18-pre2/arch/s390/vmlinux-shared.lds linux/arch/s390/vmlinux-shared.lds
--- linux-2.4.18-pre2/arch/s390/vmlinux-shared.lds	Sun Aug 12 10:38:48 2001
+++ linux/arch/s390/vmlinux-shared.lds	Thu Jan 10 13:28:33 2002
@@ -13,7 +13,6 @@
 	*(.fixup)
 	*(.gnu.warning)
 	} = 0x0700
-  .text.lock : { *(.text.lock) }	/* out-of-line lock text */
   .rodata : { *(.rodata) }
   .kstrtab : { *(.kstrtab) }
 
diff -urN linux-2.4.18-pre2/arch/s390/vmlinux.lds linux/arch/s390/vmlinux.lds
--- linux-2.4.18-pre2/arch/s390/vmlinux.lds	Mon Jul  2 14:40:58 2001
+++ linux/arch/s390/vmlinux.lds	Thu Jan 10 13:28:33 2002
@@ -13,7 +13,6 @@
 	*(.fixup)
 	*(.gnu.warning)
 	} = 0x0700
-  .text.lock : { *(.text.lock) }	/* out-of-line lock text */
   .rodata : { *(.rodata) *(.rodata.*) }
   .kstrtab : { *(.kstrtab) }
 
diff -urN linux-2.4.18-pre2/arch/s390x/vmlinux-shared.lds linux/arch/s390x/vmlinux-shared.lds
--- linux-2.4.18-pre2/arch/s390x/vmlinux-shared.lds	Sun Aug 12 10:38:48 2001
+++ linux/arch/s390x/vmlinux-shared.lds	Thu Jan 10 13:28:33 2002
@@ -13,7 +13,6 @@
 	*(.fixup)
 	*(.gnu.warning)
 	} = 0x0700
-  .text.lock : { *(.text.lock) }	/* out-of-line lock text */
   .rodata : { *(.rodata) }
   .kstrtab : { *(.kstrtab) }
 
diff -urN linux-2.4.18-pre2/arch/s390x/vmlinux.lds linux/arch/s390x/vmlinux.lds
--- linux-2.4.18-pre2/arch/s390x/vmlinux.lds	Mon Jul  2 14:40:58 2001
+++ linux/arch/s390x/vmlinux.lds	Thu Jan 10 13:28:33 2002
@@ -13,7 +13,6 @@
 	*(.fixup)
 	*(.gnu.warning)
 	} = 0x0700
-  .text.lock : { *(.text.lock) }	/* out-of-line lock text */
   .rodata : { *(.rodata) *(.rodata.*) }
   .kstrtab : { *(.kstrtab) }
 
diff -urN linux-2.4.18-pre2/arch/sh/vmlinux.lds.S linux/arch/sh/vmlinux.lds.S
--- linux-2.4.18-pre2/arch/sh/vmlinux.lds.S	Sat Sep  8 12:29:09 2001
+++ linux/arch/sh/vmlinux.lds.S	Thu Jan 10 13:28:33 2002
@@ -23,7 +23,6 @@
 	*(.fixup)
 	*(.gnu.warning)
 	} = 0x0009
-  .text.lock : { *(.text.lock) }	/* out-of-line lock text */
   .rodata : { *(.rodata) *(.rodata.*) }
   .kstrtab : { *(.kstrtab) }
 
diff -urN linux-2.4.18-pre2/drivers/char/Config.in linux/drivers/char/Config.in
--- linux-2.4.18-pre2/drivers/char/Config.in	Mon Nov 12 09:34:16 2001
+++ linux/drivers/char/Config.in	Thu Jan 10 13:28:33 2002
@@ -219,7 +219,18 @@
    bool '  Serverworks LE/HE support' CONFIG_AGP_SWORKS
 fi
 
-source drivers/char/drm/Config.in
+bool 'Direct Rendering Manager (XFree86 DRI support)' CONFIG_DRM
+if [ "$CONFIG_DRM" = "y" ]; then
+   bool '  Build drivers for old (XFree 4.0) DRM' CONFIG_DRM_OLD
+   if [ "$CONFIG_DRM_OLD" = "y" ]; then
+      comment 'DRM 4.0 drivers'
+      source drivers/char/drm-4.0/Config.in
+   else
+      comment 'DRM 4.1 drivers'
+      define_bool CONFIG_DRM_NEW y
+      source drivers/char/drm/Config.in
+   fi
+fi
 
 if [ "$CONFIG_HOTPLUG" = "y" -a "$CONFIG_PCMCIA" != "n" ]; then
    source drivers/char/pcmcia/Config.in
diff -urN linux-2.4.18-pre2/drivers/char/Makefile linux/drivers/char/Makefile
--- linux-2.4.18-pre2/drivers/char/Makefile	Sun Nov 11 10:09:32 2001
+++ linux/drivers/char/Makefile	Thu Jan 10 13:28:33 2002
@@ -25,7 +25,7 @@
 			misc.o pty.o random.o selection.o serial.o \
 			sonypi.o tty_io.o tty_ioctl.o generic_serial.o
 
-mod-subdirs	:=	joystick ftape drm pcmcia
+mod-subdirs	:=	joystick ftape drm drm-4.0 pcmcia
 
 list-multi	:=	
 
@@ -200,7 +200,8 @@
 obj-$(CONFIG_QIC02_TAPE) += tpqic02.o
 
 subdir-$(CONFIG_FTAPE) += ftape
-subdir-$(CONFIG_DRM) += drm
+subdir-$(CONFIG_DRM_OLD) += drm-4.0
+subdir-$(CONFIG_DRM_NEW) += drm
 subdir-$(CONFIG_PCMCIA) += pcmcia
 subdir-$(CONFIG_AGP) += agp
 
diff -urN linux-2.4.18-pre2/drivers/char/cyclades.c linux/drivers/char/cyclades.c
--- linux-2.4.18-pre2/drivers/char/cyclades.c	Fri Sep 14 14:04:07 2001
+++ linux/drivers/char/cyclades.c	Thu Jan 10 13:28:33 2002
@@ -4965,6 +4965,8 @@
   uclong		Ze_addr0[NR_CARDS], Ze_addr2[NR_CARDS], ZeIndex = 0;
   uclong		Ze_phys0[NR_CARDS], Ze_phys2[NR_CARDS];
   unsigned char         Ze_irq[NR_CARDS];
+  struct resource *resource;
+  unsigned long res_start, res_len;
 
         for (i = 0; i < NR_CARDS; i++) {
                 /* look for a Cyclades card by vendor and device id */
@@ -4992,8 +4994,8 @@
 
 		device_id &= ~PCI_DEVICE_ID_MASK;
 
-    if ((device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo)
-	   || (device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi)){
+	if ((device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo)
+	    || (device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi)){
 #ifdef CY_PCI_DEBUG
             printk("Cyclom-Y/PCI (bus=0x0%x, pci_id=0x%x, ",
 		pdev->bus->number, pdev->devfn);
@@ -5012,7 +5014,14 @@
 		/* Although we don't use this I/O region, we should
 		   request it from the kernel anyway, to avoid problems
 		   with other drivers accessing it. */
-		request_region(cy_pci_phys1, CyPCI_Yctl, "Cyclom-Y");
+		resource = request_region(cy_pci_phys1, CyPCI_Yctl, "Cyclom-Y");
+		if (resource == NULL) {
+		    printk(KERN_ERR "cyclades: failed to allocate IO "
+				    "resource at 0x%lx\n", cy_pci_phys1);
+		    continue;
+		}
+		res_start = cy_pci_phys1;
+		res_len = CyPCI_Yctl;
 
 #if defined(__alpha__)
                 if (device_id  == PCI_DEVICE_ID_CYCLOM_Y_Lo) { /* below 1M? */
@@ -5083,6 +5092,10 @@
                 cy_card[j].bus_index = 1;
                 cy_card[j].first_line = cy_next_channel;
                 cy_card[j].num_chips = cy_pci_nchan/4;
+		cy_card[j].resource = resource;
+		cy_card[j].res_start = res_start;
+		cy_card[j].res_len = res_len;
+		resource = NULL;	/* For next card */
 
                 /* enable interrupts in the PCI interface */
 		plx_ver = cy_readb(cy_pci_addr2 + CyPLX_VER) & 0x0f;
@@ -5118,7 +5131,7 @@
 		    cy_pci_nchan, cy_next_channel);
 
                 cy_next_channel += cy_pci_nchan;
-    }else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Lo){
+	}else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Lo){
 	    /* print message */
 		printk("Cyclades-Z/PCI (bus=0x0%x, pci_id=0x%x, ",
 		    pdev->bus->number, pdev->devfn);
@@ -5128,7 +5141,7 @@
 		    (ulong)cy_pci_phys2, (ulong)cy_pci_phys0);
 	    printk("Cyclades-Z/PCI not supported for low addresses\n");
 	    break;
-    }else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi){
+	}else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi){
 #ifdef CY_PCI_DEBUG
             printk("Cyclades-Z/PCI (bus=0x0%x, pci_id=0x%x, ",
 	        pdev->bus->number, pdev->devfn);
@@ -5163,6 +5176,15 @@
 		   request it from the kernel anyway, to avoid problems
 		   with other drivers accessing it. */
 		request_region(cy_pci_phys1, CyPCI_Zctl, "Cyclades-Z");
+		resource = request_region(cy_pci_phys1, CyPCI_Zctl, 
+					  "Cyclades-Z");
+		if (resource == NULL) {
+		    printk(KERN_ERR "cyclades: failed to allocate IO "
+				    "resource at 0x%lx\n", cy_pci_phys1);
+		    continue;
+		}
+		res_start = cy_pci_phys1;
+		res_len = CyPCI_Zctl;
 
 		if (mailbox == ZE_V1) {
 		    cy_pci_addr2 = (ulong)ioremap(cy_pci_phys2, CyPCI_Ze_win);
@@ -5261,6 +5283,10 @@
                 cy_card[j].bus_index = 1;
                 cy_card[j].first_line = cy_next_channel;
                 cy_card[j].num_chips = -1;
+		cy_card[j].resource = resource;
+		cy_card[j].res_start = res_start;
+		cy_card[j].res_len = res_len;
+		resource = NULL;	/* For next card */
 
                 /* print message */
 #ifdef CONFIG_CYZ_INTR
@@ -5279,7 +5305,7 @@
                 printk("%d channels starting from port %d.\n",
 		    cy_pci_nchan,cy_next_channel);
                 cy_next_channel += cy_pci_nchan;
-    }
+	    }
         }
 
         for (; ZeIndex != 0 && i < NR_CARDS; i++) {
@@ -5787,6 +5813,10 @@
 #endif /* CONFIG_CYZ_INTR */
 	    )
 		free_irq(cy_card[i].irq, &cy_card[i]);
+	    if (cy_card[i].resource) {
+		cy_card[i].resource = NULL;
+		release_region(cy_card[i].res_start, cy_card[i].res_len);
+	    }
         }
     }
     if (tmp_buf) {
diff -urN linux-2.4.18-pre2/drivers/char/drm/Config.in linux/drivers/char/drm/Config.in
--- linux-2.4.18-pre2/drivers/char/drm/Config.in	Fri Dec 21 09:41:53 2001
+++ linux/drivers/char/drm/Config.in	Thu Jan 10 13:28:33 2002
@@ -5,13 +5,10 @@
 # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
 #
 
-bool 'Direct Rendering Manager (XFree86 4.1.0 and higher DRI support)' CONFIG_DRM
-if [ "$CONFIG_DRM" != "n" ]; then
-    tristate '  3dfx Banshee/Voodoo3+' CONFIG_DRM_TDFX
-    tristate '  3dlabs GMX 2000' CONFIG_DRM_GAMMA
-    tristate '  ATI Rage 128' CONFIG_DRM_R128
-    dep_tristate '  ATI Radeon' CONFIG_DRM_RADEON $CONFIG_AGP
-    dep_tristate '  Intel I810' CONFIG_DRM_I810 $CONFIG_AGP
-    dep_tristate '  Matrox g200/g400' CONFIG_DRM_MGA $CONFIG_AGP
-    dep_tristate '  SiS' CONFIG_DRM_SIS $CONFIG_AGP
-fi
+tristate '  3dfx Banshee/Voodoo3+' CONFIG_DRM_TDFX
+tristate '  3dlabs GMX 2000' CONFIG_DRM_GAMMA
+tristate '  ATI Rage 128' CONFIG_DRM_R128
+dep_tristate '  ATI Radeon' CONFIG_DRM_RADEON $CONFIG_AGP
+dep_tristate '  Intel I810' CONFIG_DRM_I810 $CONFIG_AGP
+dep_tristate '  Matrox g200/g400' CONFIG_DRM_MGA $CONFIG_AGP
+dep_tristate '  SiS' CONFIG_DRM_SIS $CONFIG_AGP
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/Config.in linux/drivers/char/drm-4.0/Config.in
--- linux-2.4.18-pre2/drivers/char/drm-4.0/Config.in	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/Config.in	Thu Jan 10 13:28:33 2002
@@ -0,0 +1,13 @@
+#
+# drm device configuration
+#
+# This driver provides support for the
+# Direct Rendering Infrastructure (DRI) in XFree86 4.0 (old version).
+#
+
+tristate '  3dfx Banshee/Voodoo3+' CONFIG_DRM40_TDFX
+tristate '  3dlabs GMX 2000' CONFIG_DRM40_GAMMA
+dep_tristate '  ATI Rage 128' CONFIG_DRM40_R128 $CONFIG_AGP
+dep_tristate '  ATI Radeon' CONFIG_DRM40_RADEON $CONFIG_AGP
+dep_tristate '  Intel I810' CONFIG_DRM40_I810 $CONFIG_AGP
+dep_tristate '  Matrox G200/G400/G450' CONFIG_DRM40_MGA $CONFIG_AGP
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/Makefile linux/drivers/char/drm-4.0/Makefile
--- linux-2.4.18-pre2/drivers/char/drm-4.0/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/Makefile	Thu Jan 10 13:28:33 2002
@@ -0,0 +1,60 @@
+#
+# Makefile for the drm device driver.  This driver provides support for
+# the Direct Rendering Infrastructure (DRI) in XFree86 4.x.
+#
+
+O_TARGET	:= drm.o
+
+
+list-multi  := drmlib.o gamma.o tdfx.o r128.o ffb.o mga.o i810.o
+gamma-objs  := gamma_drv.o  gamma_dma.o
+tdfx-objs   := tdfx_drv.o                 tdfx_context.o
+r128-objs   := r128_drv.o   r128_cce.o    r128_context.o r128_bufs.o r128_state.o
+ffb-objs    := ffb_drv.o                  ffb_context.o
+mga-objs    := mga_drv.o    mga_dma.o     mga_context.o  mga_bufs.o  mga_state.o
+i810-objs   := i810_drv.o   i810_dma.o    i810_context.o i810_bufs.o
+radeon-objs := radeon_drv.o radeon_cp.o   radeon_context.o radeon_bufs.o radeon_state.o
+
+drmlib-objs	:= init.o memory.o proc.o auth.o context.o drawable.o bufs.o \
+			lists.o lock.o ioctl.o fops.o vm.o dma.o ctxbitmap.o
+
+ifneq ($(CONFIG_AGP),)
+ifneq ($(CONFIG_AGP),n)
+drmlib-objs 	+= agpsupport.o
+endif
+endif
+
+obj-$(CONFIG_DRM40_GAMMA)	+= gamma.o	drmlib.o
+obj-$(CONFIG_DRM40_TDFX)	+= tdfx.o	drmlib.o
+obj-$(CONFIG_DRM40_R128)	+= r128.o	drmlib.o
+obj-$(CONFIG_DRM40_RADEON)	+= radeon.o	drmlib.o
+obj-$(CONFIG_DRM40_FFB)		+= ffb.o	drmlib.o
+obj-$(CONFIG_DRM40_MGA)		+= mga.o	drmlib.o
+obj-$(CONFIG_DRM40_I810)	+= i810.o	drmlib.o
+
+
+include $(TOPDIR)/Rules.make
+
+drmlib.o: $(drmlib-objs)
+	$(LD) -r -o $@ $(drmlib-objs)
+
+gamma.o: $(gamma-objs)
+	$(LD) -r -o $@ $(gamma-objs)
+
+tdfx.o: $(tdfx-objs)
+	$(LD) -r -o $@ $(tdfx-objs)
+
+mga.o: $(mga-objs)
+	$(LD) -r -o $@ $(mga-objs)
+
+i810.o: $(i810-objs)
+	$(LD) -r -o $@ $(i810-objs)
+
+r128.o: $(r128-objs)
+	$(LD) -r -o $@ $(r128-objs)
+
+radeon.o: $(radeon-objs)
+	$(LD) -r -o $@ $(radeon-objs)
+
+ffb.o: $(ffb-objs)
+	$(LD) -r -o $@ $(ffb-objs)
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/README.drm linux/drivers/char/drm-4.0/README.drm
--- linux-2.4.18-pre2/drivers/char/drm-4.0/README.drm	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/README.drm	Thu Jan 10 13:28:33 2002
@@ -0,0 +1,46 @@
+************************************************************
+* For the very latest on DRI development, please see:      *
+*     http://dri.sourceforge.net/                          *
+************************************************************
+
+The Direct Rendering Manager (drm) is a device-independent kernel-level
+device driver that provides support for the XFree86 Direct Rendering
+Infrastructure (DRI).
+
+The DRM supports the Direct Rendering Infrastructure (DRI) in four major
+ways:
+
+    1. The DRM provides synchronized access to the graphics hardware via
+       the use of an optimized two-tiered lock.
+
+    2. The DRM enforces the DRI security policy for access to the graphics
+       hardware by only allowing authenticated X11 clients access to
+       restricted regions of memory.
+
+    3. The DRM provides a generic DMA engine, complete with multiple
+       queues and the ability to detect the need for an OpenGL context
+       switch.
+
+    4. The DRM is extensible via the use of small device-specific modules
+       that rely extensively on the API exported by the DRM module.
+
+
+Documentation on the DRI is available from:
+    http://precisioninsight.com/piinsights.html
+
+For specific information about kernel-level support, see:
+
+    The Direct Rendering Manager, Kernel Support for the Direct Rendering
+    Infrastructure
+    http://precisioninsight.com/dr/drm.html
+
+    Hardware Locking for the Direct Rendering Infrastructure
+    http://precisioninsight.com/dr/locking.html
+
+    A Security Analysis of the Direct Rendering Infrastructure
+    http://precisioninsight.com/dr/security.html
+
+************************************************************
+* For the very latest on DRI development, please see:      *
+*     http://dri.sourceforge.net/                          *
+************************************************************
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/agpsupport.c linux/drivers/char/drm-4.0/agpsupport.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/agpsupport.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/agpsupport.c	Thu Jan 10 13:28:33 2002
@@ -0,0 +1,339 @@
+/* agpsupport.c -- DRM support for AGP/GART backend -*- linux-c -*-
+ * Created: Mon Dec 13 09:56:45 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Author: Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include <linux/module.h>
+#if LINUX_VERSION_CODE < 0x020400
+#include "agpsupport-pre24.h"
+#else
+#define DRM_AGP_GET (drm_agp_t *)inter_module_get("drm_agp")
+#define DRM_AGP_PUT inter_module_put("drm_agp")
+#endif
+
+static const drm_agp_t *drm_agp = NULL;
+
+int drm_agp_info(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	agp_kern_info    *kern;
+	drm_agp_info_t   info;
+
+	if (!dev->agp->acquired || !drm_agp->copy_info) return -EINVAL;
+
+	kern                   = &dev->agp->agp_info;
+	info.agp_version_major = kern->version.major;
+	info.agp_version_minor = kern->version.minor;
+	info.mode              = kern->mode;
+	info.aperture_base     = kern->aper_base;
+	info.aperture_size     = kern->aper_size * 1024 * 1024;
+	info.memory_allowed    = kern->max_memory << PAGE_SHIFT;
+	info.memory_used       = kern->current_memory << PAGE_SHIFT;
+	info.id_vendor         = kern->device->vendor;
+	info.id_device         = kern->device->device;
+
+	if (copy_to_user((drm_agp_info_t *)arg, &info, sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+int drm_agp_acquire(struct inode *inode, struct file *filp, unsigned int cmd,
+		    unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	int              retcode;
+
+	if (dev->agp->acquired || !drm_agp->acquire) return -EINVAL;
+	if ((retcode = drm_agp->acquire())) return retcode;
+	dev->agp->acquired = 1;
+	return 0;
+}
+
+int drm_agp_release(struct inode *inode, struct file *filp, unsigned int cmd,
+		    unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+
+	if (!dev->agp->acquired || !drm_agp->release) return -EINVAL;
+	drm_agp->release();
+	dev->agp->acquired = 0;
+	return 0;
+	
+}
+
+void _drm_agp_release(void)
+{
+	if (drm_agp->release) drm_agp->release();
+}
+
+int drm_agp_enable(struct inode *inode, struct file *filp, unsigned int cmd,
+		   unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_agp_mode_t   mode;
+
+	if (!dev->agp->acquired || !drm_agp->enable) return -EINVAL;
+
+	if (copy_from_user(&mode, (drm_agp_mode_t *)arg, sizeof(mode)))
+		return -EFAULT;
+	
+	dev->agp->mode    = mode.mode;
+	drm_agp->enable(mode.mode);
+	dev->agp->base    = dev->agp->agp_info.aper_base;
+	dev->agp->enabled = 1;
+	return 0;
+}
+
+int drm_agp_alloc(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_agp_buffer_t request;
+	drm_agp_mem_t    *entry;
+	agp_memory       *memory;
+	unsigned long    pages;
+	u32 		 type;
+	if (!dev->agp->acquired) return -EINVAL;
+	if (copy_from_user(&request, (drm_agp_buffer_t *)arg, sizeof(request)))
+		return -EFAULT;
+	if (!(entry = drm_alloc(sizeof(*entry), DRM_MEM_AGPLISTS)))
+		return -ENOMEM;
+   
+   	memset(entry, 0, sizeof(*entry));
+
+	pages = (request.size + PAGE_SIZE - 1) / PAGE_SIZE;
+	type = (u32) request.type;
+
+	if (!(memory = drm_alloc_agp(pages, type))) {
+		drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS);
+		return -ENOMEM;
+	}
+	
+	entry->handle    = (unsigned long)memory->memory;
+	entry->memory    = memory;
+	entry->bound     = 0;
+	entry->pages     = pages;
+	entry->prev      = NULL;
+	entry->next      = dev->agp->memory;
+	if (dev->agp->memory) dev->agp->memory->prev = entry;
+	dev->agp->memory = entry;
+
+	request.handle   = entry->handle;
+        request.physical = memory->physical;
+
+	if (copy_to_user((drm_agp_buffer_t *)arg, &request, sizeof(request))) {
+		dev->agp->memory       = entry->next;
+		dev->agp->memory->prev = NULL;
+		drm_free_agp(memory, pages);
+		drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS);
+		return -EFAULT;
+	}
+	return 0;
+}
+
+static drm_agp_mem_t *drm_agp_lookup_entry(drm_device_t *dev,
+					   unsigned long handle)
+{
+	drm_agp_mem_t *entry;
+
+	for (entry = dev->agp->memory; entry; entry = entry->next) {
+		if (entry->handle == handle) return entry;
+	}
+	return NULL;
+}
+
+int drm_agp_unbind(struct inode *inode, struct file *filp, unsigned int cmd,
+		   unsigned long arg)
+{
+	drm_file_t	  *priv	 = filp->private_data;
+	drm_device_t	  *dev	 = priv->dev;
+	drm_agp_binding_t request;
+	drm_agp_mem_t     *entry;
+
+	if (!dev->agp->acquired) return -EINVAL;
+	if (copy_from_user(&request, (drm_agp_binding_t *)arg, sizeof(request)))
+		return -EFAULT;
+	if (!(entry = drm_agp_lookup_entry(dev, request.handle)))
+		return -EINVAL;
+	if (!entry->bound) return -EINVAL;
+	return drm_unbind_agp(entry->memory);
+}
+
+int drm_agp_bind(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	  *priv	 = filp->private_data;
+	drm_device_t	  *dev	 = priv->dev;
+	drm_agp_binding_t request;
+	drm_agp_mem_t     *entry;
+	int               retcode;
+	int               page;
+	
+	if (!dev->agp->acquired || !drm_agp->bind_memory) return -EINVAL;
+	if (copy_from_user(&request, (drm_agp_binding_t *)arg, sizeof(request)))
+		return -EFAULT;
+	if (!(entry = drm_agp_lookup_entry(dev, request.handle)))
+		return -EINVAL;
+	if (entry->bound) return -EINVAL;
+	page = (request.offset + PAGE_SIZE - 1) / PAGE_SIZE;
+	if ((retcode = drm_bind_agp(entry->memory, page))) return retcode;
+	entry->bound = dev->agp->base + (page << PAGE_SHIFT);
+	DRM_DEBUG("base = 0x%lx entry->bound = 0x%lx\n", 
+		  dev->agp->base, entry->bound);
+	return 0;
+}
+
+int drm_agp_free(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_agp_buffer_t request;
+	drm_agp_mem_t    *entry;
+	
+	if (!dev->agp->acquired) return -EINVAL;
+	if (copy_from_user(&request, (drm_agp_buffer_t *)arg, sizeof(request)))
+		return -EFAULT;
+	if (!(entry = drm_agp_lookup_entry(dev, request.handle)))
+		return -EINVAL;
+	if (entry->bound) drm_unbind_agp(entry->memory);
+   
+	if (entry->prev) entry->prev->next = entry->next;
+	else             dev->agp->memory  = entry->next;
+	if (entry->next) entry->next->prev = entry->prev;
+	drm_free_agp(entry->memory, entry->pages);
+	drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS);
+	return 0;
+}
+
+drm_agp_head_t *drm_agp_init(void)
+{
+	drm_agp_head_t *head         = NULL;
+
+	drm_agp = DRM_AGP_GET;
+	if (drm_agp) {
+		if (!(head = drm_alloc(sizeof(*head), DRM_MEM_AGPLISTS)))
+			return NULL;
+		memset((void *)head, 0, sizeof(*head));
+		drm_agp->copy_info(&head->agp_info);
+		if (head->agp_info.chipset == NOT_SUPPORTED) {
+			drm_free(head, sizeof(*head), DRM_MEM_AGPLISTS);
+			return NULL;
+		}
+		head->memory = NULL;
+		switch (head->agp_info.chipset) {
+		case INTEL_GENERIC:	head->chipset = "Intel";         break;
+		case INTEL_LX:		head->chipset = "Intel 440LX";   break;
+		case INTEL_BX:		head->chipset = "Intel 440BX";   break;
+		case INTEL_GX:		head->chipset = "Intel 440GX";   break;
+		case INTEL_I810:	head->chipset = "Intel i810";    break;
+
+#if LINUX_VERSION_CODE >= 0x020400
+		case INTEL_I840:	head->chipset = "Intel i840";    break;
+#endif
+
+		case VIA_GENERIC:	head->chipset = "VIA";           break;
+		case VIA_VP3:		head->chipset = "VIA VP3";       break;
+		case VIA_MVP3:		head->chipset = "VIA MVP3";      break;
+
+#if LINUX_VERSION_CODE >= 0x020400
+		case VIA_MVP4:		head->chipset = "VIA MVP4";      break;
+		case VIA_APOLLO_KX133:	head->chipset = "VIA Apollo KX133"; 
+			break;
+		case VIA_APOLLO_KT133:	head->chipset = "VIA Apollo KT133"; 
+			break;
+#endif
+
+		case VIA_APOLLO_PRO: 	head->chipset = "VIA Apollo Pro";
+			break;
+		case SIS_GENERIC:	head->chipset = "SiS";           break;
+		case AMD_GENERIC:	head->chipset = "AMD";           break;
+		case AMD_IRONGATE:	head->chipset = "AMD Irongate";  break;
+		case ALI_GENERIC:	head->chipset = "ALi";           break;
+		case ALI_M1541: 	head->chipset = "ALi M1541";     break;
+		case ALI_M1621: 	head->chipset = "ALi M1621";	 break;
+		case ALI_M1631: 	head->chipset = "ALi M1631";	 break;
+		case ALI_M1632: 	head->chipset = "ALi M1632";	 break;
+		case ALI_M1641: 	head->chipset = "ALi M1641";	 break;
+		case ALI_M1647: 	head->chipset = "ALi M1647";	 break;
+		case ALI_M1651: 	head->chipset = "ALi M1651";	 break;
+		case SVWRKS_GENERIC:	head->chipset = "Serverworks Generic";
+			break;
+		case SVWRKS_HE: 	head->chipset = "Serverworks HE"; break;
+		case SVWRKS_LE: 	head->chipset = "Serverworks LE"; break;
+
+		default:		head->chipset = "Unknown";       break;
+		}
+		DRM_INFO("AGP %d.%d on %s @ 0x%08lx %ZuMB\n",
+			 head->agp_info.version.major,
+			 head->agp_info.version.minor,
+			 head->chipset,
+			 head->agp_info.aper_base,
+			 head->agp_info.aper_size);
+	}
+	return head;
+}
+
+void drm_agp_uninit(void)
+{
+	DRM_AGP_PUT;
+	drm_agp = NULL;
+}
+
+agp_memory *drm_agp_allocate_memory(size_t pages, u32 type)
+{
+	if (!drm_agp->allocate_memory) return NULL;
+	return drm_agp->allocate_memory(pages, type);
+}
+
+int drm_agp_free_memory(agp_memory *handle)
+{
+	if (!handle || !drm_agp->free_memory) return 0;
+	drm_agp->free_memory(handle);
+	return 1;
+}
+
+int drm_agp_bind_memory(agp_memory *handle, off_t start)
+{
+	if (!handle || !drm_agp->bind_memory) return -EINVAL;
+	return drm_agp->bind_memory(handle, start);
+}
+
+int drm_agp_unbind_memory(agp_memory *handle)
+{
+	if (!handle || !drm_agp->unbind_memory) return -EINVAL;
+	return drm_agp->unbind_memory(handle);
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/auth.c linux/drivers/char/drm-4.0/auth.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/auth.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/auth.c	Thu Jan 10 13:28:33 2002
@@ -0,0 +1,162 @@
+/* auth.c -- IOCTLs for authentication -*- linux-c -*-
+ * Created: Tue Feb  2 08:37:54 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+static int drm_hash_magic(drm_magic_t magic)
+{
+	return magic & (DRM_HASH_SIZE-1);
+}
+
+static drm_file_t *drm_find_file(drm_device_t *dev, drm_magic_t magic)
+{
+	drm_file_t	  *retval = NULL;
+	drm_magic_entry_t *pt;
+	int		  hash	  = drm_hash_magic(magic);
+
+	down(&dev->struct_sem);
+	for (pt = dev->magiclist[hash].head; pt; pt = pt->next) {
+		if (pt->magic == magic) {
+			retval = pt->priv;
+			break;
+		}
+	}
+	up(&dev->struct_sem);
+	return retval;
+}
+
+int drm_add_magic(drm_device_t *dev, drm_file_t *priv, drm_magic_t magic)
+{
+	int		  hash;
+	drm_magic_entry_t *entry;
+	
+	DRM_DEBUG("%d\n", magic);
+	
+	hash	     = drm_hash_magic(magic);
+	entry	     = drm_alloc(sizeof(*entry), DRM_MEM_MAGIC);
+	if (!entry) return -ENOMEM;
+	entry->magic = magic;
+	entry->priv  = priv;
+	entry->next  = NULL;
+
+	down(&dev->struct_sem);
+	if (dev->magiclist[hash].tail) {
+		dev->magiclist[hash].tail->next = entry;
+		dev->magiclist[hash].tail	= entry;
+	} else {
+		dev->magiclist[hash].head	= entry;
+		dev->magiclist[hash].tail	= entry;
+	}
+	up(&dev->struct_sem);
+	
+	return 0;
+}
+
+int drm_remove_magic(drm_device_t *dev, drm_magic_t magic)
+{
+	drm_magic_entry_t *prev = NULL;
+	drm_magic_entry_t *pt;
+	int		  hash;
+	
+	DRM_DEBUG("%d\n", magic);
+	hash = drm_hash_magic(magic);
+	
+	down(&dev->struct_sem);
+	for (pt = dev->magiclist[hash].head; pt; prev = pt, pt = pt->next) {
+		if (pt->magic == magic) {
+			if (dev->magiclist[hash].head == pt) {
+				dev->magiclist[hash].head = pt->next;
+			}
+			if (dev->magiclist[hash].tail == pt) {
+				dev->magiclist[hash].tail = prev;
+			}
+			if (prev) {
+				prev->next = pt->next;
+			}
+			up(&dev->struct_sem);
+			return 0;
+		}
+	}
+	up(&dev->struct_sem);
+
+	drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
+	
+	return -EINVAL;
+}
+
+int drm_getmagic(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	static drm_magic_t sequence = 0;
+	static spinlock_t  lock	    = SPIN_LOCK_UNLOCKED;
+	drm_file_t	   *priv    = filp->private_data;
+	drm_device_t	   *dev	    = priv->dev;
+	drm_auth_t	   auth;
+
+				/* Find unique magic */
+	if (priv->magic) {
+		auth.magic = priv->magic;
+	} else {
+		do {
+			spin_lock(&lock);
+			if (!sequence) ++sequence; /* reserve 0 */
+			auth.magic = sequence++;
+			spin_unlock(&lock);
+		} while (drm_find_file(dev, auth.magic));
+		priv->magic = auth.magic;
+		drm_add_magic(dev, priv, auth.magic);
+	}
+	
+	DRM_DEBUG("%u\n", auth.magic);
+	if (copy_to_user((drm_auth_t *)arg, &auth, sizeof(auth)))
+		return -EFAULT;
+	return 0;
+}
+
+int drm_authmagic(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	   *priv    = filp->private_data;
+	drm_device_t	   *dev	    = priv->dev;
+	drm_auth_t	   auth;
+	drm_file_t	   *file;
+
+	if (copy_from_user(&auth, (drm_auth_t *)arg, sizeof(auth)))
+		return -EFAULT;
+	DRM_DEBUG("%u\n", auth.magic);
+	if ((file = drm_find_file(dev, auth.magic))) {
+		file->authenticated = 1;
+		drm_remove_magic(dev, auth.magic);
+		return 0;
+	}
+	return -EINVAL;
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/bufs.c linux/drivers/char/drm-4.0/bufs.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/bufs.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/bufs.c	Thu Jan 10 13:28:33 2002
@@ -0,0 +1,543 @@
+/* bufs.c -- IOCTLs to manage buffers -*- linux-c -*-
+ * Created: Tue Feb  2 08:37:54 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include "drmP.h"
+#include "linux/un.h"
+
+				/* Compute order.  Can be made faster. */
+int drm_order(unsigned long size)
+{
+	int	      order;
+	unsigned long tmp;
+
+	for (order = 0, tmp = size; tmp >>= 1; ++order);
+	if (size & ~(1 << order)) ++order;
+	return order;
+}
+
+int drm_addmap(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_map_t	*map;
+	
+	if (!(filp->f_mode & 3)) return -EACCES; /* Require read/write */
+
+	map	     = drm_alloc(sizeof(*map), DRM_MEM_MAPS);
+	if (!map) return -ENOMEM;
+	if (copy_from_user(map, (drm_map_t *)arg, sizeof(*map))) {
+		drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+		return -EFAULT;
+	}
+
+	DRM_DEBUG("offset = 0x%08lx, size = 0x%08lx, type = %d\n",
+		  map->offset, map->size, map->type);
+	if ((map->offset & (~PAGE_MASK)) || (map->size & (~PAGE_MASK))) {
+		drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+		return -EINVAL;
+	}
+	map->mtrr   = -1;
+	map->handle = 0;
+
+	switch (map->type) {
+	case _DRM_REGISTERS:
+	case _DRM_FRAME_BUFFER:
+#ifndef __sparc__
+		if (map->offset + map->size < map->offset
+		    || map->offset < virt_to_phys(high_memory)) {
+			drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+			return -EINVAL;
+		}
+#endif
+#ifdef CONFIG_MTRR
+		if (map->type == _DRM_FRAME_BUFFER
+		    || (map->flags & _DRM_WRITE_COMBINING)) {
+			map->mtrr = mtrr_add(map->offset, map->size,
+					     MTRR_TYPE_WRCOMB, 1);
+		}
+#endif
+		map->handle = drm_ioremap(map->offset, map->size);
+		break;
+			
+
+	case _DRM_SHM:
+		map->handle = (void *)drm_alloc_pages(drm_order(map->size)
+						      - PAGE_SHIFT,
+						      DRM_MEM_SAREA);
+		DRM_DEBUG("%ld %d %p\n", map->size, drm_order(map->size),
+			  map->handle);
+		if (!map->handle) {
+			drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+			return -ENOMEM;
+		}
+		map->offset = (unsigned long)map->handle;
+		if (map->flags & _DRM_CONTAINS_LOCK) {
+			dev->lock.hw_lock = map->handle; /* Pointer to lock */
+		}
+		break;
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	case _DRM_AGP:
+		map->offset = map->offset + dev->agp->base;
+		break;
+#endif
+	default:
+		drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+		return -EINVAL;
+	}
+
+	down(&dev->struct_sem);
+	if (dev->maplist) {
+		++dev->map_count;
+		dev->maplist = drm_realloc(dev->maplist,
+					   (dev->map_count-1)
+					   * sizeof(*dev->maplist),
+					   dev->map_count
+					   * sizeof(*dev->maplist),
+					   DRM_MEM_MAPS);
+	} else {
+		dev->map_count = 1;
+		dev->maplist = drm_alloc(dev->map_count*sizeof(*dev->maplist),
+					 DRM_MEM_MAPS);
+	}
+	dev->maplist[dev->map_count-1] = map;
+	up(&dev->struct_sem);
+
+	if (copy_to_user((drm_map_t *)arg, map, sizeof(*map)))
+		return -EFAULT;
+	if (map->type != _DRM_SHM) {
+		if (copy_to_user(&((drm_map_t *)arg)->handle,
+				 &map->offset,
+				 sizeof(map->offset)))
+			return -EFAULT;
+	}		
+	return 0;
+}
+
+int drm_addbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_desc_t	 request;
+	int		 count;
+	int		 order;
+	int		 size;
+	int		 total;
+	int		 page_order;
+	drm_buf_entry_t	 *entry;
+	unsigned long	 page;
+	drm_buf_t	 *buf;
+	int		 alignment;
+	unsigned long	 offset;
+	int		 i;
+	int		 byte_count;
+	int		 page_count;
+
+	if (!dma) return -EINVAL;
+
+	if (copy_from_user(&request,
+			   (drm_buf_desc_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	count	   = request.count;
+	order	   = drm_order(request.size);
+	size	   = 1 << order;
+	
+	DRM_DEBUG("count = %d, size = %d (%d), order = %d, queue_count = %d\n",
+		  request.count, request.size, size, order, dev->queue_count);
+
+	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+	if (dev->queue_count) return -EBUSY; /* Not while in use */
+
+	alignment  = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size):size;
+	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
+	total	   = PAGE_SIZE << page_order;
+
+	spin_lock(&dev->count_lock);
+	if (dev->buf_use) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	atomic_inc(&dev->buf_alloc);
+	spin_unlock(&dev->count_lock);
+	
+	down(&dev->struct_sem);
+	entry = &dma->bufs[order];
+	if (entry->buf_count) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM;	/* May only call once for each order */
+	}
+	
+	if(count < 0 || count > 4096)
+	{
+		up(&dev->struct_sem);
+		return -EINVAL;
+	}
+	
+	entry->buflist = drm_alloc(count * sizeof(*entry->buflist),
+				   DRM_MEM_BUFS);
+	if (!entry->buflist) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM;
+	}
+	memset(entry->buflist, 0, count * sizeof(*entry->buflist));
+
+	entry->seglist = drm_alloc(count * sizeof(*entry->seglist),
+				   DRM_MEM_SEGS);
+	if (!entry->seglist) {
+		drm_free(entry->buflist,
+			 count * sizeof(*entry->buflist),
+			 DRM_MEM_BUFS);
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM;
+	}
+	memset(entry->seglist, 0, count * sizeof(*entry->seglist));
+
+	dma->pagelist = drm_realloc(dma->pagelist,
+				    dma->page_count * sizeof(*dma->pagelist),
+				    (dma->page_count + (count << page_order))
+				    * sizeof(*dma->pagelist),
+				    DRM_MEM_PAGES);
+	DRM_DEBUG("pagelist: %d entries\n",
+		  dma->page_count + (count << page_order));
+
+
+	entry->buf_size	  = size;
+	entry->page_order = page_order;
+	byte_count	  = 0;
+	page_count	  = 0;
+	while (entry->buf_count < count) {
+		if (!(page = drm_alloc_pages(page_order, DRM_MEM_DMA))) break;
+		entry->seglist[entry->seg_count++] = page;
+		for (i = 0; i < (1 << page_order); i++) {
+			DRM_DEBUG("page %d @ 0x%08lx\n",
+				  dma->page_count + page_count,
+				  page + PAGE_SIZE * i);
+			dma->pagelist[dma->page_count + page_count++]
+				= page + PAGE_SIZE * i;
+		}
+		for (offset = 0;
+		     offset + size <= total && entry->buf_count < count;
+		     offset += alignment, ++entry->buf_count) {
+			buf	     = &entry->buflist[entry->buf_count];
+			buf->idx     = dma->buf_count + entry->buf_count;
+			buf->total   = alignment;
+			buf->order   = order;
+			buf->used    = 0;
+			buf->offset  = (dma->byte_count + byte_count + offset);
+			buf->address = (void *)(page + offset);
+			buf->next    = NULL;
+			buf->waiting = 0;
+			buf->pending = 0;
+			init_waitqueue_head(&buf->dma_wait);
+			buf->pid     = 0;
+#if DRM_DMA_HISTOGRAM
+			buf->time_queued     = 0;
+			buf->time_dispatched = 0;
+			buf->time_completed  = 0;
+			buf->time_freed	     = 0;
+#endif
+			DRM_DEBUG("buffer %d @ %p\n",
+				  entry->buf_count, buf->address);
+		}
+		byte_count += PAGE_SIZE << page_order;
+	}
+
+	dma->buflist = drm_realloc(dma->buflist,
+				   dma->buf_count * sizeof(*dma->buflist),
+				   (dma->buf_count + entry->buf_count)
+				   * sizeof(*dma->buflist),
+				   DRM_MEM_BUFS);
+	for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++)
+		dma->buflist[i] = &entry->buflist[i - dma->buf_count];
+
+	dma->buf_count	+= entry->buf_count;
+	dma->seg_count	+= entry->seg_count;
+	dma->page_count += entry->seg_count << page_order;
+	dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order);
+	
+	drm_freelist_create(&entry->freelist, entry->buf_count);
+	for (i = 0; i < entry->buf_count; i++) {
+		drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]);
+	}
+	
+	up(&dev->struct_sem);
+
+	request.count = entry->buf_count;
+	request.size  = size;
+
+	if (copy_to_user((drm_buf_desc_t *)arg,
+			 &request,
+			 sizeof(request)))
+		return -EFAULT;
+	
+	atomic_dec(&dev->buf_alloc);
+	return 0;
+}
+
+int drm_infobufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_info_t	 request;
+	int		 i;
+	int		 count;
+
+	if (!dma) return -EINVAL;
+
+	spin_lock(&dev->count_lock);
+	if (atomic_read(&dev->buf_alloc)) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	++dev->buf_use;		/* Can't allocate more after this call */
+	spin_unlock(&dev->count_lock);
+
+	if (copy_from_user(&request,
+			   (drm_buf_info_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) {
+		if (dma->bufs[i].buf_count) ++count;
+	}
+	
+	DRM_DEBUG("count = %d\n", count);
+	
+	if (request.count >= count) {
+		for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) {
+			if (dma->bufs[i].buf_count) {
+				if (copy_to_user(&request.list[count].count,
+						 &dma->bufs[i].buf_count,
+						 sizeof(dma->bufs[0]
+							.buf_count)) ||
+				    copy_to_user(&request.list[count].size,
+						 &dma->bufs[i].buf_size,
+						 sizeof(dma->bufs[0].buf_size)) ||
+				    copy_to_user(&request.list[count].low_mark,
+						 &dma->bufs[i]
+						 .freelist.low_mark,
+						 sizeof(dma->bufs[0]
+							.freelist.low_mark)) ||
+				    copy_to_user(&request.list[count]
+						 .high_mark,
+						 &dma->bufs[i]
+						 .freelist.high_mark,
+						 sizeof(dma->bufs[0]
+							.freelist.high_mark)))
+					return -EFAULT;
+
+				DRM_DEBUG("%d %d %d %d %d\n",
+					  i,
+					  dma->bufs[i].buf_count,
+					  dma->bufs[i].buf_size,
+					  dma->bufs[i].freelist.low_mark,
+					  dma->bufs[i].freelist.high_mark);
+				++count;
+			}
+		}
+	}
+	request.count = count;
+
+	if (copy_to_user((drm_buf_info_t *)arg,
+			 &request,
+			 sizeof(request)))
+		return -EFAULT;
+	
+	return 0;
+}
+
+int drm_markbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_desc_t	 request;
+	int		 order;
+	drm_buf_entry_t	 *entry;
+
+	if (!dma) return -EINVAL;
+
+	if (copy_from_user(&request,
+			   (drm_buf_desc_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	DRM_DEBUG("%d, %d, %d\n",
+		  request.size, request.low_mark, request.high_mark);
+	order = drm_order(request.size);
+	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+	entry = &dma->bufs[order];
+
+	if (request.low_mark < 0 || request.low_mark > entry->buf_count)
+		return -EINVAL;
+	if (request.high_mark < 0 || request.high_mark > entry->buf_count)
+		return -EINVAL;
+
+	entry->freelist.low_mark  = request.low_mark;
+	entry->freelist.high_mark = request.high_mark;
+	
+	return 0;
+}
+
+int drm_freebufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_free_t	 request;
+	int		 i;
+	int		 idx;
+	drm_buf_t	 *buf;
+
+	if (!dma) return -EINVAL;
+
+	if (copy_from_user(&request,
+			   (drm_buf_free_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	DRM_DEBUG("%d\n", request.count);
+	for (i = 0; i < request.count; i++) {
+		if (copy_from_user(&idx,
+				   &request.list[i],
+				   sizeof(idx)))
+			return -EFAULT;
+		if (idx < 0 || idx >= dma->buf_count) {
+			DRM_ERROR("Index %d (of %d max)\n",
+				  idx, dma->buf_count - 1);
+			return -EINVAL;
+		}
+		buf = dma->buflist[idx];
+		if (buf->pid != current->pid) {
+			DRM_ERROR("Process %d freeing buffer owned by %d\n",
+				  current->pid, buf->pid);
+			return -EINVAL;
+		}
+		drm_free_buffer(dev, buf);
+	}
+	
+	return 0;
+}
+
+int drm_mapbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	int		 retcode = 0;
+	const int	 zero	 = 0;
+	unsigned long	 virtual;
+	unsigned long	 address;
+	drm_buf_map_t	 request;
+	int		 i;
+
+	if (!dma) return -EINVAL;
+	
+	DRM_DEBUG("\n");
+
+	spin_lock(&dev->count_lock);
+	if (atomic_read(&dev->buf_alloc)) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	++dev->buf_use;		/* Can't allocate more after this call */
+	spin_unlock(&dev->count_lock);
+
+	if (copy_from_user(&request,
+			   (drm_buf_map_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	if (request.count >= dma->buf_count) {
+		down_write(&current->mm->mmap_sem);
+		virtual = do_mmap(filp, 0, dma->byte_count,
+				  PROT_READ|PROT_WRITE, MAP_SHARED, 0);
+		up_write(&current->mm->mmap_sem);
+		if (virtual > -1024UL) {
+				/* Real error */
+			retcode = (signed long)virtual;
+			goto done;
+		}
+		request.virtual = (void *)virtual;
+
+		for (i = 0; i < dma->buf_count; i++) {
+			if (copy_to_user(&request.list[i].idx,
+					 &dma->buflist[i]->idx,
+					 sizeof(request.list[0].idx))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			if (copy_to_user(&request.list[i].total,
+					 &dma->buflist[i]->total,
+					 sizeof(request.list[0].total))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			if (copy_to_user(&request.list[i].used,
+					 &zero,
+					 sizeof(zero))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			address = virtual + dma->buflist[i]->offset;
+			if (copy_to_user(&request.list[i].address,
+					 &address,
+					 sizeof(address))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+		}
+	}
+done:
+	request.count = dma->buf_count;
+	DRM_DEBUG("%d buffers, retcode = %d\n", request.count, retcode);
+
+	if (copy_to_user((drm_buf_map_t *)arg,
+			 &request,
+			 sizeof(request)))
+		return -EFAULT;
+
+	return retcode;
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/context.c linux/drivers/char/drm-4.0/context.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/context.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/context.c	Thu Jan 10 13:28:33 2002
@@ -0,0 +1,321 @@
+/* context.c -- IOCTLs for contexts and DMA queues -*- linux-c -*-
+ * Created: Tue Feb  2 08:37:54 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+static int drm_init_queue(drm_device_t *dev, drm_queue_t *q, drm_ctx_t *ctx)
+{
+	DRM_DEBUG("\n");
+	
+	if (atomic_read(&q->use_count) != 1
+	    || atomic_read(&q->finalization)
+	    || atomic_read(&q->block_count)) {
+		DRM_ERROR("New queue is already in use: u%d f%d b%d\n",
+			  atomic_read(&q->use_count),
+			  atomic_read(&q->finalization),
+			  atomic_read(&q->block_count));
+	}
+		  
+	atomic_set(&q->finalization,  0);
+	atomic_set(&q->block_count,   0);
+	atomic_set(&q->block_read,    0);
+	atomic_set(&q->block_write,   0);
+	atomic_set(&q->total_queued,  0);
+	atomic_set(&q->total_flushed, 0);
+	atomic_set(&q->total_locks,   0);
+
+	init_waitqueue_head(&q->write_queue);
+	init_waitqueue_head(&q->read_queue);
+	init_waitqueue_head(&q->flush_queue);
+
+	q->flags = ctx->flags;
+
+	drm_waitlist_create(&q->waitlist, dev->dma->buf_count);
+
+	return 0;
+}
+
+
+/* drm_alloc_queue:
+PRE: 1) dev->queuelist[0..dev->queue_count] is allocated and will not
+	disappear (so all deallocation must be done after IOCTLs are off)
+     2) dev->queue_count < dev->queue_slots
+     3) dev->queuelist[i].use_count == 0 and
+	dev->queuelist[i].finalization == 0 if i not in use 
+POST: 1) dev->queuelist[i].use_count == 1
+      2) dev->queue_count < dev->queue_slots */
+		
+static int drm_alloc_queue(drm_device_t *dev)
+{
+	int	    i;
+	drm_queue_t *queue;
+	int	    oldslots;
+	int	    newslots;
+				/* Check for a free queue */
+	for (i = 0; i < dev->queue_count; i++) {
+		atomic_inc(&dev->queuelist[i]->use_count);
+		if (atomic_read(&dev->queuelist[i]->use_count) == 1
+		    && !atomic_read(&dev->queuelist[i]->finalization)) {
+			DRM_DEBUG("%d (free)\n", i);
+			return i;
+		}
+		atomic_dec(&dev->queuelist[i]->use_count);
+	}
+				/* Allocate a new queue */
+	
+	queue = drm_alloc(sizeof(*queue), DRM_MEM_QUEUES);
+	if(queue == NULL)
+		return -ENOMEM;	
+
+	memset(queue, 0, sizeof(*queue));
+	down(&dev->struct_sem);
+	atomic_set(&queue->use_count, 1);
+	
+	++dev->queue_count;
+	if (dev->queue_count >= dev->queue_slots) {
+		oldslots = dev->queue_slots * sizeof(*dev->queuelist);
+		if (!dev->queue_slots) dev->queue_slots = 1;
+		dev->queue_slots *= 2;
+		newslots = dev->queue_slots * sizeof(*dev->queuelist);
+
+		dev->queuelist = drm_realloc(dev->queuelist,
+					     oldslots,
+					     newslots,
+					     DRM_MEM_QUEUES);
+		if (!dev->queuelist) {
+			up(&dev->struct_sem);
+			DRM_DEBUG("out of memory\n");
+			return -ENOMEM;
+		}
+	}
+	dev->queuelist[dev->queue_count-1] = queue;
+	
+	up(&dev->struct_sem);
+	DRM_DEBUG("%d (new)\n", dev->queue_count - 1);
+	return dev->queue_count - 1;
+}
+
+int drm_resctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_ctx_res_t	res;
+	drm_ctx_t	ctx;
+	int		i;
+
+	DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS);
+	if (copy_from_user(&res, (drm_ctx_res_t *)arg, sizeof(res)))
+		return -EFAULT;
+	if (res.count >= DRM_RESERVED_CONTEXTS) {
+		memset(&ctx, 0, sizeof(ctx));
+		for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
+			ctx.handle = i;
+			if (copy_to_user(&res.contexts[i],
+					 &i,
+					 sizeof(i)))
+				return -EFAULT;
+		}
+	}
+	res.count = DRM_RESERVED_CONTEXTS;
+	if (copy_to_user((drm_ctx_res_t *)arg, &res, sizeof(res)))
+		return -EFAULT;
+	return 0;
+}
+
+
+int drm_addctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	if ((ctx.handle = drm_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) {
+				/* Init kernel's context and get a new one. */
+		drm_init_queue(dev, dev->queuelist[ctx.handle], &ctx);
+		ctx.handle = drm_alloc_queue(dev);
+	}
+	drm_init_queue(dev, dev->queuelist[ctx.handle], &ctx);
+	DRM_DEBUG("%d\n", ctx.handle);
+	if (copy_to_user((drm_ctx_t *)arg, &ctx, sizeof(ctx)))
+		return -EFAULT;
+	return 0;
+}
+
+int drm_modctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+	drm_queue_t	*q;
+		
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	
+	DRM_DEBUG("%d\n", ctx.handle);
+	
+	if (ctx.handle < 0 || ctx.handle >= dev->queue_count) return -EINVAL;
+	q = dev->queuelist[ctx.handle];
+	
+	atomic_inc(&q->use_count);
+	if (atomic_read(&q->use_count) == 1) {
+				/* No longer in use */
+		atomic_dec(&q->use_count);
+		return -EINVAL;
+	}
+
+	if (DRM_BUFCOUNT(&q->waitlist)) {
+		atomic_dec(&q->use_count);
+		return -EBUSY;
+	}
+	
+	q->flags = ctx.flags;
+	
+	atomic_dec(&q->use_count);
+	return 0;
+}
+
+int drm_getctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+	drm_queue_t	*q;
+		
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	
+	DRM_DEBUG("%d\n", ctx.handle);
+	
+	if (ctx.handle >= dev->queue_count) return -EINVAL;
+	q = dev->queuelist[ctx.handle];
+	
+	atomic_inc(&q->use_count);
+	if (atomic_read(&q->use_count) == 1) {
+				/* No longer in use */
+		atomic_dec(&q->use_count);
+		return -EINVAL;
+	}
+	
+	ctx.flags = q->flags;
+	atomic_dec(&q->use_count);
+	
+	if (copy_to_user((drm_ctx_t *)arg, &ctx, sizeof(ctx)))
+		return -EFAULT;
+	
+	return 0;
+}
+
+int drm_switchctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	return drm_context_switch(dev, dev->last_context, ctx.handle);
+}
+
+int drm_newctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	drm_context_switch_complete(dev, ctx.handle);
+
+	return 0;
+}
+
+int drm_rmctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	      unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+	drm_queue_t	*q;
+	drm_buf_t	*buf;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	
+	if (ctx.handle >= dev->queue_count) return -EINVAL;
+	q = dev->queuelist[ctx.handle];
+	
+	atomic_inc(&q->use_count);
+	if (atomic_read(&q->use_count) == 1) {
+				/* No longer in use */
+		atomic_dec(&q->use_count);
+		return -EINVAL;
+	}
+	
+	atomic_inc(&q->finalization); /* Mark queue in finalization state */
+	atomic_sub(2, &q->use_count); /* Mark queue as unused (pending
+					 finalization) */
+
+	while (test_and_set_bit(0, &dev->interrupt_flag)) {
+		schedule();
+		if (signal_pending(current)) {
+			clear_bit(0, &dev->interrupt_flag);
+			return -EINTR;
+		}
+	}
+				/* Remove queued buffers */
+	while ((buf = drm_waitlist_get(&q->waitlist))) {
+		drm_free_buffer(dev, buf);
+	}
+	clear_bit(0, &dev->interrupt_flag);
+	
+				/* Wakeup blocked processes */
+	wake_up_interruptible(&q->read_queue);
+	wake_up_interruptible(&q->write_queue);
+	wake_up_interruptible(&q->flush_queue);
+	
+				/* Finalization over.  Queue is made
+				   available when both use_count and
+				   finalization become 0, which won't
+				   happen until all the waiting processes
+				   stop waiting. */
+	atomic_dec(&q->finalization);
+	return 0;
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/ctxbitmap.c linux/drivers/char/drm-4.0/ctxbitmap.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/ctxbitmap.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/ctxbitmap.c	Thu Jan 10 13:28:33 2002
@@ -0,0 +1,85 @@
+/* ctxbitmap.c -- Context bitmap management -*- linux-c -*-
+ * Created: Thu Jan 6 03:56:42 2000 by jhartmann@precisioninsight.com
+ * 
+ * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Author: Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+void drm_ctxbitmap_free(drm_device_t *dev, int ctx_handle)
+{
+	if (ctx_handle < 0) goto failed;
+
+	if (ctx_handle < DRM_MAX_CTXBITMAP) {
+		clear_bit(ctx_handle, dev->ctx_bitmap);
+		return;
+	}
+failed:
+       	DRM_ERROR("Attempt to free invalid context handle: %d\n",
+		  ctx_handle);
+       	return;
+}
+
+int drm_ctxbitmap_next(drm_device_t *dev)
+{
+	int bit;
+
+	bit = find_first_zero_bit(dev->ctx_bitmap, DRM_MAX_CTXBITMAP);
+	if (bit < DRM_MAX_CTXBITMAP) {
+		set_bit(bit, dev->ctx_bitmap);
+	   	DRM_DEBUG("drm_ctxbitmap_next bit : %d\n", bit);
+		return bit;
+	}
+	return -1;
+}
+
+int drm_ctxbitmap_init(drm_device_t *dev)
+{
+	int i;
+   	int temp;
+
+	dev->ctx_bitmap = (unsigned long *) drm_alloc(PAGE_SIZE, 
+						      DRM_MEM_CTXBITMAP);
+	if(dev->ctx_bitmap == NULL) {
+		return -ENOMEM;
+	}
+	memset((void *) dev->ctx_bitmap, 0, PAGE_SIZE);
+	for(i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
+		temp = drm_ctxbitmap_next(dev);
+	   	DRM_DEBUG("drm_ctxbitmap_init : %d\n", temp);
+	}
+
+	return 0;
+}
+
+void drm_ctxbitmap_cleanup(drm_device_t *dev)
+{
+	drm_free((void *)dev->ctx_bitmap, PAGE_SIZE,
+		 DRM_MEM_CTXBITMAP);
+}
+
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/dma.c linux/drivers/char/drm-4.0/dma.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/dma.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/dma.c	Thu Jan 10 13:28:33 2002
@@ -0,0 +1,546 @@
+/* dma.c -- DMA IOCTL and function support -*- linux-c -*-
+ * Created: Fri Mar 19 14:30:16 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinuxa.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+#include <linux/interrupt.h>	/* For task queue support */
+
+void drm_dma_setup(drm_device_t *dev)
+{
+	int i;
+	
+	if (!(dev->dma = drm_alloc(sizeof(*dev->dma), DRM_MEM_DRIVER))) {
+                printk(KERN_ERR "drm_dma_setup: can't drm_alloc dev->dma");
+                return;
+        }       
+	memset(dev->dma, 0, sizeof(*dev->dma));
+	for (i = 0; i <= DRM_MAX_ORDER; i++)
+		memset(&dev->dma->bufs[i], 0, sizeof(dev->dma->bufs[0]));
+}
+
+void drm_dma_takedown(drm_device_t *dev)
+{
+	drm_device_dma_t  *dma = dev->dma;
+	int		  i, j;
+
+	if (!dma) return;
+	
+				/* Clear dma buffers */
+	for (i = 0; i <= DRM_MAX_ORDER; i++) {
+		if (dma->bufs[i].seg_count) {
+			DRM_DEBUG("order %d: buf_count = %d,"
+				  " seg_count = %d\n",
+				  i,
+				  dma->bufs[i].buf_count,
+				  dma->bufs[i].seg_count);
+			for (j = 0; j < dma->bufs[i].seg_count; j++) {
+				drm_free_pages(dma->bufs[i].seglist[j],
+					       dma->bufs[i].page_order,
+					       DRM_MEM_DMA);
+			}
+			drm_free(dma->bufs[i].seglist,
+				 dma->bufs[i].seg_count
+				 * sizeof(*dma->bufs[0].seglist),
+				 DRM_MEM_SEGS);
+		}
+	   	if(dma->bufs[i].buf_count) {
+		   	for(j = 0; j < dma->bufs[i].buf_count; j++) {
+			   if(dma->bufs[i].buflist[j].dev_private) {
+			      drm_free(dma->bufs[i].buflist[j].dev_private,
+				       dma->bufs[i].buflist[j].dev_priv_size,
+				       DRM_MEM_BUFS);
+			   }
+			}
+		   	drm_free(dma->bufs[i].buflist,
+				 dma->bufs[i].buf_count *
+				 sizeof(*dma->bufs[0].buflist),
+				 DRM_MEM_BUFS);
+		   	drm_freelist_destroy(&dma->bufs[i].freelist);
+		}
+	}
+	
+	if (dma->buflist) {
+		drm_free(dma->buflist,
+			 dma->buf_count * sizeof(*dma->buflist),
+			 DRM_MEM_BUFS);
+	}
+
+	if (dma->pagelist) {
+		drm_free(dma->pagelist,
+			 dma->page_count * sizeof(*dma->pagelist),
+			 DRM_MEM_PAGES);
+	}
+	drm_free(dev->dma, sizeof(*dev->dma), DRM_MEM_DRIVER);
+	dev->dma = NULL;
+}
+
+#if DRM_DMA_HISTOGRAM
+/* This is slow, but is useful for debugging. */
+int drm_histogram_slot(unsigned long count)
+{
+	int value = DRM_DMA_HISTOGRAM_INITIAL;
+	int slot;
+
+	for (slot = 0;
+	     slot < DRM_DMA_HISTOGRAM_SLOTS;
+	     ++slot, value = DRM_DMA_HISTOGRAM_NEXT(value)) {
+		if (count < value) return slot;
+	}
+	return DRM_DMA_HISTOGRAM_SLOTS - 1;
+}
+
+void drm_histogram_compute(drm_device_t *dev, drm_buf_t *buf)
+{
+	cycles_t queued_to_dispatched;
+	cycles_t dispatched_to_completed;
+	cycles_t completed_to_freed;
+	int	 q2d, d2c, c2f, q2c, q2f;
+	
+	if (buf->time_queued) {
+		queued_to_dispatched	= (buf->time_dispatched
+					   - buf->time_queued);
+		dispatched_to_completed = (buf->time_completed
+					   - buf->time_dispatched);
+		completed_to_freed	= (buf->time_freed
+					   - buf->time_completed);
+
+		q2d = drm_histogram_slot(queued_to_dispatched);
+		d2c = drm_histogram_slot(dispatched_to_completed);
+		c2f = drm_histogram_slot(completed_to_freed);
+
+		q2c = drm_histogram_slot(queued_to_dispatched
+					 + dispatched_to_completed);
+		q2f = drm_histogram_slot(queued_to_dispatched
+					 + dispatched_to_completed
+					 + completed_to_freed);
+		
+		atomic_inc(&dev->histo.total);
+		atomic_inc(&dev->histo.queued_to_dispatched[q2d]);
+		atomic_inc(&dev->histo.dispatched_to_completed[d2c]);
+		atomic_inc(&dev->histo.completed_to_freed[c2f]);
+		
+		atomic_inc(&dev->histo.queued_to_completed[q2c]);
+		atomic_inc(&dev->histo.queued_to_freed[q2f]);
+
+	}
+	buf->time_queued     = 0;
+	buf->time_dispatched = 0;
+	buf->time_completed  = 0;
+	buf->time_freed	     = 0;
+}
+#endif
+
+void drm_free_buffer(drm_device_t *dev, drm_buf_t *buf)
+{
+	drm_device_dma_t *dma = dev->dma;
+
+	if (!buf) return;
+	
+	buf->waiting  = 0;
+	buf->pending  = 0;
+	buf->pid      = 0;
+	buf->used     = 0;
+#if DRM_DMA_HISTOGRAM
+	buf->time_completed = get_cycles();
+#endif
+	if (waitqueue_active(&buf->dma_wait)) {
+		wake_up_interruptible(&buf->dma_wait);
+	} else {
+				/* If processes are waiting, the last one
+				   to wake will put the buffer on the free
+				   list.  If no processes are waiting, we
+				   put the buffer on the freelist here. */
+		drm_freelist_put(dev, &dma->bufs[buf->order].freelist, buf);
+	}
+}
+
+void drm_reclaim_buffers(drm_device_t *dev, pid_t pid)
+{
+	drm_device_dma_t *dma = dev->dma;
+	int		 i;
+
+	if (!dma) return;
+	for (i = 0; i < dma->buf_count; i++) {
+		if (dma->buflist[i]->pid == pid) {
+			switch (dma->buflist[i]->list) {
+			case DRM_LIST_NONE:
+				drm_free_buffer(dev, dma->buflist[i]);
+				break;
+			case DRM_LIST_WAIT:
+				dma->buflist[i]->list = DRM_LIST_RECLAIM;
+				break;
+			default:
+				/* Buffer already on hardware. */
+				break;
+			}
+		}
+	}
+}
+
+int drm_context_switch(drm_device_t *dev, int old, int new)
+{
+	char	    buf[64];
+	drm_queue_t *q;
+
+	atomic_inc(&dev->total_ctx);
+
+	if (test_and_set_bit(0, &dev->context_flag)) {
+		DRM_ERROR("Reentering -- FIXME\n");
+		return -EBUSY;
+	}
+
+#if DRM_DMA_HISTOGRAM
+	dev->ctx_start = get_cycles();
+#endif
+	
+	DRM_DEBUG("Context switch from %d to %d\n", old, new);
+
+	if (new >= dev->queue_count) {
+		clear_bit(0, &dev->context_flag);
+		return -EINVAL;
+	}
+
+	if (new == dev->last_context) {
+		clear_bit(0, &dev->context_flag);
+		return 0;
+	}
+	
+	q = dev->queuelist[new];
+	atomic_inc(&q->use_count);
+	if (atomic_read(&q->use_count) == 1) {
+		atomic_dec(&q->use_count);
+		clear_bit(0, &dev->context_flag);
+		return -EINVAL;
+	}
+
+	if (drm_flags & DRM_FLAG_NOCTX) {
+		drm_context_switch_complete(dev, new);
+	} else {
+		sprintf(buf, "C %d %d\n", old, new);
+		drm_write_string(dev, buf);
+	}
+	
+	atomic_dec(&q->use_count);
+	
+	return 0;
+}
+
+int drm_context_switch_complete(drm_device_t *dev, int new)
+{
+	drm_device_dma_t *dma = dev->dma;
+	
+	dev->last_context = new;  /* PRE/POST: This is the _only_ writer. */
+	dev->last_switch  = jiffies;
+	
+	if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("Lock isn't held after context switch\n");
+	}
+
+	if (!dma || !(dma->next_buffer && dma->next_buffer->while_locked)) {
+		if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				  DRM_KERNEL_CONTEXT)) {
+			DRM_ERROR("Cannot free lock\n");
+		}
+	}
+	
+#if DRM_DMA_HISTOGRAM
+	atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles()
+						      - dev->ctx_start)]);
+		   
+#endif
+	clear_bit(0, &dev->context_flag);
+	wake_up_interruptible(&dev->context_wait);
+	
+	return 0;
+}
+
+void drm_clear_next_buffer(drm_device_t *dev)
+{
+	drm_device_dma_t *dma = dev->dma;
+	
+	dma->next_buffer = NULL;
+	if (dma->next_queue && !DRM_BUFCOUNT(&dma->next_queue->waitlist)) {
+		wake_up_interruptible(&dma->next_queue->flush_queue);
+	}
+	dma->next_queue	 = NULL;
+}
+
+
+int drm_select_queue(drm_device_t *dev, void (*wrapper)(unsigned long))
+{
+	int	   i;
+	int	   candidate = -1;
+	int	   j	     = jiffies;
+
+	if (!dev) {
+		DRM_ERROR("No device\n");
+		return -1;
+	}
+	if (!dev->queuelist || !dev->queuelist[DRM_KERNEL_CONTEXT]) {
+				/* This only happens between the time the
+				   interrupt is initialized and the time
+				   the queues are initialized. */
+		return -1;
+	}
+
+				/* Doing "while locked" DMA? */
+	if (DRM_WAITCOUNT(dev, DRM_KERNEL_CONTEXT)) {
+		return DRM_KERNEL_CONTEXT;
+	}
+
+				/* If there are buffers on the last_context
+				   queue, and we have not been executing
+				   this context very long, continue to
+				   execute this context. */
+	if (dev->last_switch <= j
+	    && dev->last_switch + DRM_TIME_SLICE > j
+	    && DRM_WAITCOUNT(dev, dev->last_context)) {
+		return dev->last_context;
+	}
+
+				/* Otherwise, find a candidate */
+	for (i = dev->last_checked + 1; i < dev->queue_count; i++) {
+		if (DRM_WAITCOUNT(dev, i)) {
+			candidate = dev->last_checked = i;
+			break;
+		}
+	}
+
+	if (candidate < 0) {
+		for (i = 0; i < dev->queue_count; i++) {
+			if (DRM_WAITCOUNT(dev, i)) {
+				candidate = dev->last_checked = i;
+				break;
+			}
+		}
+	}
+
+	if (wrapper
+	    && candidate >= 0
+	    && candidate != dev->last_context
+	    && dev->last_switch <= j
+	    && dev->last_switch + DRM_TIME_SLICE > j) {
+		if (dev->timer.expires != dev->last_switch + DRM_TIME_SLICE) {
+			del_timer(&dev->timer);
+			dev->timer.function = wrapper;
+			dev->timer.data	    = (unsigned long)dev;
+			dev->timer.expires  = dev->last_switch+DRM_TIME_SLICE;
+			add_timer(&dev->timer);
+		}
+		return -1;
+	}
+
+	return candidate;
+}
+
+
+int drm_dma_enqueue(drm_device_t *dev, drm_dma_t *d)
+{
+	int		  i;
+	drm_queue_t	  *q;
+	drm_buf_t	  *buf;
+	int		  idx;
+	int		  while_locked = 0;
+	drm_device_dma_t  *dma = dev->dma;
+	DECLARE_WAITQUEUE(entry, current);
+
+	DRM_DEBUG("%d\n", d->send_count);
+
+	if (d->flags & _DRM_DMA_WHILE_LOCKED) {
+		int context = dev->lock.hw_lock->lock;
+		
+		if (!_DRM_LOCK_IS_HELD(context)) {
+			DRM_ERROR("No lock held during \"while locked\""
+				  " request\n");
+			return -EINVAL;
+		}
+		if (d->context != _DRM_LOCKING_CONTEXT(context)
+		    && _DRM_LOCKING_CONTEXT(context) != DRM_KERNEL_CONTEXT) {
+			DRM_ERROR("Lock held by %d while %d makes"
+				  " \"while locked\" request\n",
+				  _DRM_LOCKING_CONTEXT(context),
+				  d->context);
+			return -EINVAL;
+		}
+		q = dev->queuelist[DRM_KERNEL_CONTEXT];
+		while_locked = 1;
+	} else {
+		q = dev->queuelist[d->context];
+	}
+
+
+	atomic_inc(&q->use_count);
+	if (atomic_read(&q->block_write)) {
+		add_wait_queue(&q->write_queue, &entry);
+		atomic_inc(&q->block_count);
+		for (;;) {
+			current->state = TASK_INTERRUPTIBLE;
+			if (!atomic_read(&q->block_write)) break;
+			schedule();
+			if (signal_pending(current)) {
+				atomic_dec(&q->use_count);
+				remove_wait_queue(&q->write_queue, &entry);
+				return -EINTR;
+			}
+		}
+		atomic_dec(&q->block_count);
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&q->write_queue, &entry);
+	}
+	
+	for (i = 0; i < d->send_count; i++) {
+		idx = d->send_indices[i];
+		if (idx < 0 || idx >= dma->buf_count) {
+			atomic_dec(&q->use_count);
+			DRM_ERROR("Index %d (of %d max)\n",
+				  d->send_indices[i], dma->buf_count - 1);
+			return -EINVAL;
+		}
+		buf = dma->buflist[ idx ];
+		if (buf->pid != current->pid) {
+			atomic_dec(&q->use_count);
+			DRM_ERROR("Process %d using buffer owned by %d\n",
+				  current->pid, buf->pid);
+			return -EINVAL;
+		}
+		if (buf->list != DRM_LIST_NONE) {
+			atomic_dec(&q->use_count);
+			DRM_ERROR("Process %d using buffer %d on list %d\n",
+				  current->pid, buf->idx, buf->list);
+		}
+		buf->used	  = d->send_sizes[i];
+		buf->while_locked = while_locked;
+		buf->context	  = d->context;
+		if (!buf->used) {
+			DRM_ERROR("Queueing 0 length buffer\n");
+		}
+		if (buf->pending) {
+			atomic_dec(&q->use_count);
+			DRM_ERROR("Queueing pending buffer:"
+				  " buffer %d, offset %d\n",
+				  d->send_indices[i], i);
+			return -EINVAL;
+		}
+		if (buf->waiting) {
+			atomic_dec(&q->use_count);
+			DRM_ERROR("Queueing waiting buffer:"
+				  " buffer %d, offset %d\n",
+				  d->send_indices[i], i);
+			return -EINVAL;
+		}
+		buf->waiting = 1;
+		if (atomic_read(&q->use_count) == 1
+		    || atomic_read(&q->finalization)) {
+			drm_free_buffer(dev, buf);
+		} else {
+			drm_waitlist_put(&q->waitlist, buf);
+			atomic_inc(&q->total_queued);
+		}
+	}
+	atomic_dec(&q->use_count);
+	
+	return 0;
+}
+
+static int drm_dma_get_buffers_of_order(drm_device_t *dev, drm_dma_t *d,
+					int order)
+{
+	int		  i;
+	drm_buf_t	  *buf;
+	drm_device_dma_t  *dma = dev->dma;
+	
+	for (i = d->granted_count; i < d->request_count; i++) {
+		buf = drm_freelist_get(&dma->bufs[order].freelist,
+				       d->flags & _DRM_DMA_WAIT);
+		if (!buf) break;
+		if (buf->pending || buf->waiting) {
+			DRM_ERROR("Free buffer %d in use by %d (w%d, p%d)\n",
+				  buf->idx,
+				  buf->pid,
+				  buf->waiting,
+				  buf->pending);
+		}
+		buf->pid     = current->pid;
+		if (copy_to_user(&d->request_indices[i],
+				 &buf->idx,
+				 sizeof(buf->idx)))
+			return -EFAULT;
+
+		if (copy_to_user(&d->request_sizes[i],
+				 &buf->total,
+				 sizeof(buf->total)))
+			return -EFAULT;
+
+		++d->granted_count;
+	}
+	return 0;
+}
+
+
+int drm_dma_get_buffers(drm_device_t *dev, drm_dma_t *dma)
+{
+	int		  order;
+	int		  retcode = 0;
+	int		  tmp_order;
+	
+	order = drm_order(dma->request_size);
+
+	dma->granted_count = 0;
+	retcode		   = drm_dma_get_buffers_of_order(dev, dma, order);
+
+	if (dma->granted_count < dma->request_count
+	    && (dma->flags & _DRM_DMA_SMALLER_OK)) {
+		for (tmp_order = order - 1;
+		     !retcode
+			     && dma->granted_count < dma->request_count
+			     && tmp_order >= DRM_MIN_ORDER;
+		     --tmp_order) {
+			
+			retcode = drm_dma_get_buffers_of_order(dev, dma,
+							       tmp_order);
+		}
+	}
+
+	if (dma->granted_count < dma->request_count
+	    && (dma->flags & _DRM_DMA_LARGER_OK)) {
+		for (tmp_order = order + 1;
+		     !retcode
+			     && dma->granted_count < dma->request_count
+			     && tmp_order <= DRM_MAX_ORDER;
+		     ++tmp_order) {
+			
+			retcode = drm_dma_get_buffers_of_order(dev, dma,
+							       tmp_order);
+		}
+	}
+	return 0;
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/drawable.c linux/drivers/char/drm-4.0/drawable.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/drawable.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/drawable.c	Thu Jan 10 13:28:33 2002
@@ -0,0 +1,51 @@
+/* drawable.c -- IOCTLs for drawables -*- linux-c -*-
+ * Created: Tue Feb  2 08:37:54 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+int drm_adddraw(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_draw_t draw;
+
+	draw.handle = 0;	/* NOOP */
+	DRM_DEBUG("%d\n", draw.handle);
+	if (copy_to_user((drm_draw_t *)arg, &draw, sizeof(draw)))
+		return -EFAULT;
+	return 0;
+}
+
+int drm_rmdraw(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	return 0;		/* NOOP */
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/drm.h linux/drivers/char/drm-4.0/drm.h
--- linux-2.4.18-pre2/drivers/char/drm-4.0/drm.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/drm.h	Thu Jan 10 13:28:33 2002
@@ -0,0 +1,414 @@
+/* drm.h -- Header for Direct Rendering Manager -*- linux-c -*-
+ * Created: Mon Jan  4 10:05:05 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ * Acknowledgements:
+ * Dec 1999, Richard Henderson <rth@twiddle.net>, move to generic cmpxchg.
+ *
+ */
+
+#ifndef _DRM_H_
+#define _DRM_H_
+
+#include <linux/config.h>
+#if defined(__linux__)
+#include <asm/ioctl.h>		/* For _IO* macros */
+#define DRM_IOCTL_NR(n)	     _IOC_NR(n)
+#elif defined(__FreeBSD__)
+#include <sys/ioccom.h>
+#define DRM_IOCTL_NR(n)	     ((n) & 0xff)
+#endif
+
+#define DRM_PROC_DEVICES "/proc/devices"
+#define DRM_PROC_MISC	 "/proc/misc"
+#define DRM_PROC_DRM	 "/proc/drm"
+#define DRM_DEV_DRM	 "/dev/drm"
+#define DRM_DEV_MODE	 (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP)
+#define DRM_DEV_UID	 0
+#define DRM_DEV_GID	 0
+
+
+#define DRM_NAME	"drm"	  /* Name in kernel, /dev, and /proc	    */
+#define DRM_MIN_ORDER	5	  /* At least 2^5 bytes = 32 bytes	    */
+#define DRM_MAX_ORDER	22	  /* Up to 2^22 bytes = 4MB		    */
+#define DRM_RAM_PERCENT 10	  /* How much system ram can we lock?	    */
+
+#define _DRM_LOCK_HELD	0x80000000 /* Hardware lock is held		    */
+#define _DRM_LOCK_CONT	0x40000000 /* Hardware lock is contended	    */
+#define _DRM_LOCK_IS_HELD(lock)	   ((lock) & _DRM_LOCK_HELD)
+#define _DRM_LOCK_IS_CONT(lock)	   ((lock) & _DRM_LOCK_CONT)
+#define _DRM_LOCKING_CONTEXT(lock) ((lock) & ~(_DRM_LOCK_HELD|_DRM_LOCK_CONT))
+
+typedef unsigned long drm_handle_t;
+typedef unsigned int  drm_context_t;
+typedef unsigned int  drm_drawable_t;
+typedef unsigned int  drm_magic_t;
+
+/* Warning: If you change this structure, make sure you change
+ * XF86DRIClipRectRec in the server as well */
+
+typedef struct drm_clip_rect {
+           unsigned short x1;
+           unsigned short y1;
+           unsigned short x2;
+           unsigned short y2;
+} drm_clip_rect_t;
+
+/* Seperate include files for the i810/mga/r128 specific structures */
+#include "mga_drm.h"
+#include "i810_drm.h"
+#include "r128_drm.h"
+#include "radeon_drm.h"
+#ifdef CONFIG_DRM40_SIS
+#include "sis_drm.h"
+#endif
+
+typedef struct drm_version {
+	int    version_major;	  /* Major version			    */
+	int    version_minor;	  /* Minor version			    */
+	int    version_patchlevel;/* Patch level			    */
+	size_t name_len;	  /* Length of name buffer		    */
+	char   *name;		  /* Name of driver			    */
+	size_t date_len;	  /* Length of date buffer		    */
+	char   *date;		  /* User-space buffer to hold date	    */
+	size_t desc_len;	  /* Length of desc buffer		    */
+	char   *desc;		  /* User-space buffer to hold desc	    */
+} drm_version_t;
+
+typedef struct drm_unique {
+	size_t unique_len;	  /* Length of unique			    */
+	char   *unique;		  /* Unique name for driver instantiation   */
+} drm_unique_t;
+
+typedef struct drm_list {
+	int		 count;	  /* Length of user-space structures	    */
+	drm_version_t	 *version;
+} drm_list_t;
+
+typedef struct drm_block {
+	int		 unused;
+} drm_block_t;
+
+typedef struct drm_control {
+	enum {
+		DRM_ADD_COMMAND,
+		DRM_RM_COMMAND,
+		DRM_INST_HANDLER,
+		DRM_UNINST_HANDLER
+	}		 func;
+	int		 irq;
+} drm_control_t;
+
+typedef enum drm_map_type {
+	_DRM_FRAME_BUFFER = 0,	  /* WC (no caching), no core dump	    */
+	_DRM_REGISTERS	  = 1,	  /* no caching, no core dump		    */
+	_DRM_SHM	  = 2,	  /* shared, cached			    */
+	_DRM_AGP          = 3	  /* AGP/GART                               */
+} drm_map_type_t;
+
+typedef enum drm_map_flags {
+	_DRM_RESTRICTED	     = 0x01, /* Cannot be mapped to user-virtual    */
+	_DRM_READ_ONLY	     = 0x02,
+	_DRM_LOCKED	     = 0x04, /* shared, cached, locked		    */
+	_DRM_KERNEL	     = 0x08, /* kernel requires access		    */
+	_DRM_WRITE_COMBINING = 0x10, /* use write-combining if available    */
+	_DRM_CONTAINS_LOCK   = 0x20  /* SHM page that contains lock	    */
+} drm_map_flags_t;
+
+typedef struct drm_map {
+	unsigned long	offset;	 /* Requested physical address (0 for SAREA)*/
+	unsigned long	size;	 /* Requested physical size (bytes)	    */
+	drm_map_type_t	type;	 /* Type of memory to map		    */
+	drm_map_flags_t flags;	 /* Flags				    */
+	void		*handle; /* User-space: "Handle" to pass to mmap    */
+				 /* Kernel-space: kernel-virtual address    */
+	int		mtrr;	 /* MTRR slot used			    */
+				 /* Private data			    */
+} drm_map_t;
+
+typedef enum drm_lock_flags {
+	_DRM_LOCK_READY	     = 0x01, /* Wait until hardware is ready for DMA */
+	_DRM_LOCK_QUIESCENT  = 0x02, /* Wait until hardware quiescent	     */
+	_DRM_LOCK_FLUSH	     = 0x04, /* Flush this context's DMA queue first */
+	_DRM_LOCK_FLUSH_ALL  = 0x08, /* Flush all DMA queues first	     */
+				/* These *HALT* flags aren't supported yet
+				   -- they will be used to support the
+				   full-screen DGA-like mode. */
+	_DRM_HALT_ALL_QUEUES = 0x10, /* Halt all current and future queues   */
+	_DRM_HALT_CUR_QUEUES = 0x20  /* Halt all current queues		     */
+} drm_lock_flags_t;
+
+typedef struct drm_lock {
+	int		 context;
+	drm_lock_flags_t flags;
+} drm_lock_t;
+
+typedef enum drm_dma_flags {	      /* These values *MUST* match xf86drm.h */
+				      /* Flags for DMA buffer dispatch	     */
+	_DRM_DMA_BLOCK	      = 0x01, /* Block until buffer dispatched.
+					 Note, the buffer may not yet have
+					 been processed by the hardware --
+					 getting a hardware lock with the
+					 hardware quiescent will ensure
+					 that the buffer has been
+					 processed.			     */
+	_DRM_DMA_WHILE_LOCKED = 0x02, /* Dispatch while lock held	     */
+	_DRM_DMA_PRIORITY     = 0x04, /* High priority dispatch		     */
+
+				      /* Flags for DMA buffer request	     */
+	_DRM_DMA_WAIT	      = 0x10, /* Wait for free buffers		     */
+	_DRM_DMA_SMALLER_OK   = 0x20, /* Smaller-than-requested buffers ok   */
+	_DRM_DMA_LARGER_OK    = 0x40  /* Larger-than-requested buffers ok    */
+} drm_dma_flags_t;
+
+typedef struct drm_buf_desc {
+	int	      count;	 /* Number of buffers of this size	     */
+	int	      size;	 /* Size in bytes			     */
+	int	      low_mark;	 /* Low water mark			     */
+	int	      high_mark; /* High water mark			     */
+	enum {
+		_DRM_PAGE_ALIGN = 0x01, /* Align on page boundaries for DMA  */
+		_DRM_AGP_BUFFER = 0x02  /* Buffer is in agp space            */
+	}	      flags;
+	unsigned long agp_start; /* Start address of where the agp buffers
+				  * are in the agp aperture */
+} drm_buf_desc_t;
+
+typedef struct drm_buf_info {
+	int	       count;	/* Entries in list			     */
+	drm_buf_desc_t *list;
+} drm_buf_info_t;
+
+typedef struct drm_buf_free {
+	int	       count;
+	int	       *list;
+} drm_buf_free_t;
+
+typedef struct drm_buf_pub {
+	int		  idx;	       /* Index into master buflist	     */
+	int		  total;       /* Buffer size			     */
+	int		  used;	       /* Amount of buffer in use (for DMA)  */
+	void		  *address;    /* Address of buffer		     */
+} drm_buf_pub_t;
+
+typedef struct drm_buf_map {
+	int	      count;	/* Length of buflist			    */
+	void	      *virtual;	/* Mmaped area in user-virtual		    */
+	drm_buf_pub_t *list;	/* Buffer information			    */
+} drm_buf_map_t;
+
+typedef struct drm_dma {
+				/* Indices here refer to the offset into
+				   buflist in drm_buf_get_t.  */
+	int		context;	  /* Context handle		    */
+	int		send_count;	  /* Number of buffers to send	    */
+	int		*send_indices;	  /* List of handles to buffers	    */
+	int		*send_sizes;	  /* Lengths of data to send	    */
+	drm_dma_flags_t flags;		  /* Flags			    */
+	int		request_count;	  /* Number of buffers requested    */
+	int		request_size;	  /* Desired size for buffers	    */
+	int		*request_indices; /* Buffer information		    */
+	int		*request_sizes;
+	int		granted_count;	  /* Number of buffers granted	    */
+} drm_dma_t;
+
+typedef enum {
+	_DRM_CONTEXT_PRESERVED = 0x01,
+	_DRM_CONTEXT_2DONLY    = 0x02
+} drm_ctx_flags_t;
+
+typedef struct drm_ctx {
+	drm_context_t	handle;
+	drm_ctx_flags_t flags;
+} drm_ctx_t;
+
+typedef struct drm_ctx_res {
+	int		count;
+	drm_ctx_t	*contexts;
+} drm_ctx_res_t;
+
+typedef struct drm_draw {
+	drm_drawable_t	handle;
+} drm_draw_t;
+
+typedef struct drm_auth {
+	drm_magic_t	magic;
+} drm_auth_t;
+
+typedef struct drm_irq_busid {
+	int irq;
+	int busnum;
+	int devnum;
+	int funcnum;
+} drm_irq_busid_t;
+
+typedef struct drm_agp_mode {
+	unsigned long mode;
+} drm_agp_mode_t;
+
+				/* For drm_agp_alloc -- allocated a buffer */
+typedef struct drm_agp_buffer {
+	unsigned long size;	/* In bytes -- will round to page boundary */
+	unsigned long handle;	/* Used for BIND/UNBIND ioctls */
+	unsigned long type;     /* Type of memory to allocate  */
+        unsigned long physical; /* Physical used by i810       */
+} drm_agp_buffer_t;
+
+				/* For drm_agp_bind */
+typedef struct drm_agp_binding {
+	unsigned long handle;   /* From drm_agp_buffer */
+	unsigned long offset;	/* In bytes -- will round to page boundary */
+} drm_agp_binding_t;
+
+typedef struct drm_agp_info {
+	int            agp_version_major;
+	int            agp_version_minor;
+	unsigned long  mode;
+	unsigned long  aperture_base;  /* physical address */
+	unsigned long  aperture_size;  /* bytes */
+	unsigned long  memory_allowed; /* bytes */
+	unsigned long  memory_used;
+
+				/* PCI information */
+	unsigned short id_vendor;
+	unsigned short id_device;
+} drm_agp_info_t;
+
+#define DRM_IOCTL_BASE			'd'
+#define DRM_IO(nr)			_IO(DRM_IOCTL_BASE,nr)
+#define DRM_IOR(nr,size)		_IOR(DRM_IOCTL_BASE,nr,size)
+#define DRM_IOW(nr,size)		_IOW(DRM_IOCTL_BASE,nr,size)
+#define DRM_IOWR(nr,size)		_IOWR(DRM_IOCTL_BASE,nr,size)
+
+
+#define DRM_IOCTL_VERSION		DRM_IOWR(0x00, drm_version_t)
+#define DRM_IOCTL_GET_UNIQUE		DRM_IOWR(0x01, drm_unique_t)
+#define DRM_IOCTL_GET_MAGIC		DRM_IOR( 0x02, drm_auth_t)
+#define DRM_IOCTL_IRQ_BUSID		DRM_IOWR(0x03, drm_irq_busid_t)
+
+#define DRM_IOCTL_SET_UNIQUE		DRM_IOW( 0x10, drm_unique_t)
+#define DRM_IOCTL_AUTH_MAGIC		DRM_IOW( 0x11, drm_auth_t)
+#define DRM_IOCTL_BLOCK			DRM_IOWR(0x12, drm_block_t)
+#define DRM_IOCTL_UNBLOCK		DRM_IOWR(0x13, drm_block_t)
+#define DRM_IOCTL_CONTROL		DRM_IOW( 0x14, drm_control_t)
+#define DRM_IOCTL_ADD_MAP		DRM_IOWR(0x15, drm_map_t)
+#define DRM_IOCTL_ADD_BUFS		DRM_IOWR(0x16, drm_buf_desc_t)
+#define DRM_IOCTL_MARK_BUFS		DRM_IOW( 0x17, drm_buf_desc_t)
+#define DRM_IOCTL_INFO_BUFS		DRM_IOWR(0x18, drm_buf_info_t)
+#define DRM_IOCTL_MAP_BUFS		DRM_IOWR(0x19, drm_buf_map_t)
+#define DRM_IOCTL_FREE_BUFS		DRM_IOW( 0x1a, drm_buf_free_t)
+
+#define DRM_IOCTL_ADD_CTX		DRM_IOWR(0x20, drm_ctx_t)
+#define DRM_IOCTL_RM_CTX		DRM_IOWR(0x21, drm_ctx_t)
+#define DRM_IOCTL_MOD_CTX		DRM_IOW( 0x22, drm_ctx_t)
+#define DRM_IOCTL_GET_CTX		DRM_IOWR(0x23, drm_ctx_t)
+#define DRM_IOCTL_SWITCH_CTX		DRM_IOW( 0x24, drm_ctx_t)
+#define DRM_IOCTL_NEW_CTX		DRM_IOW( 0x25, drm_ctx_t)
+#define DRM_IOCTL_RES_CTX		DRM_IOWR(0x26, drm_ctx_res_t)
+#define DRM_IOCTL_ADD_DRAW		DRM_IOWR(0x27, drm_draw_t)
+#define DRM_IOCTL_RM_DRAW		DRM_IOWR(0x28, drm_draw_t)
+#define DRM_IOCTL_DMA			DRM_IOWR(0x29, drm_dma_t)
+#define DRM_IOCTL_LOCK			DRM_IOW( 0x2a, drm_lock_t)
+#define DRM_IOCTL_UNLOCK		DRM_IOW( 0x2b, drm_lock_t)
+#define DRM_IOCTL_FINISH		DRM_IOW( 0x2c, drm_lock_t)
+
+#define DRM_IOCTL_AGP_ACQUIRE		DRM_IO(  0x30)
+#define DRM_IOCTL_AGP_RELEASE		DRM_IO(  0x31)
+#define DRM_IOCTL_AGP_ENABLE		DRM_IOW( 0x32, drm_agp_mode_t)
+#define DRM_IOCTL_AGP_INFO		DRM_IOR( 0x33, drm_agp_info_t)
+#define DRM_IOCTL_AGP_ALLOC		DRM_IOWR(0x34, drm_agp_buffer_t)
+#define DRM_IOCTL_AGP_FREE		DRM_IOW( 0x35, drm_agp_buffer_t)
+#define DRM_IOCTL_AGP_BIND		DRM_IOW( 0x36, drm_agp_binding_t)
+#define DRM_IOCTL_AGP_UNBIND		DRM_IOW( 0x37, drm_agp_binding_t)
+
+/* Mga specific ioctls */
+#define DRM_IOCTL_MGA_INIT		DRM_IOW( 0x40, drm_mga_init_t)
+#define DRM_IOCTL_MGA_SWAP		DRM_IOW( 0x41, drm_mga_swap_t)
+#define DRM_IOCTL_MGA_CLEAR		DRM_IOW( 0x42, drm_mga_clear_t)
+#define DRM_IOCTL_MGA_ILOAD		DRM_IOW( 0x43, drm_mga_iload_t)
+#define DRM_IOCTL_MGA_VERTEX		DRM_IOW( 0x44, drm_mga_vertex_t)
+#define DRM_IOCTL_MGA_FLUSH		DRM_IOW( 0x45, drm_lock_t )
+#define DRM_IOCTL_MGA_INDICES		DRM_IOW( 0x46, drm_mga_indices_t)
+#define DRM_IOCTL_MGA_BLIT		DRM_IOW( 0x47, drm_mga_blit_t)
+
+/* I810 specific ioctls */
+#define DRM_IOCTL_I810_INIT		DRM_IOW( 0x40, drm_i810_init_t)
+#define DRM_IOCTL_I810_VERTEX		DRM_IOW( 0x41, drm_i810_vertex_t)
+#define DRM_IOCTL_I810_CLEAR		DRM_IOW( 0x42, drm_i810_clear_t)
+#define DRM_IOCTL_I810_FLUSH		DRM_IO(  0x43)
+#define DRM_IOCTL_I810_GETAGE		DRM_IO(  0x44)
+#define DRM_IOCTL_I810_GETBUF		DRM_IOWR(0x45, drm_i810_dma_t)
+#define DRM_IOCTL_I810_SWAP		DRM_IO(  0x46)
+#define DRM_IOCTL_I810_COPY		DRM_IOW( 0x47, drm_i810_copy_t)
+#define DRM_IOCTL_I810_DOCOPY		DRM_IO(  0x48)
+
+/* Rage 128 specific ioctls */
+#define DRM_IOCTL_R128_INIT		DRM_IOW( 0x40, drm_r128_init_t)
+#define DRM_IOCTL_R128_CCE_START	DRM_IO(  0x41)
+#define DRM_IOCTL_R128_CCE_STOP		DRM_IOW( 0x42, drm_r128_cce_stop_t)
+#define DRM_IOCTL_R128_CCE_RESET	DRM_IO(  0x43)
+#define DRM_IOCTL_R128_CCE_IDLE		DRM_IO(  0x44)
+#define DRM_IOCTL_R128_RESET		DRM_IO(  0x46)
+#define DRM_IOCTL_R128_SWAP		DRM_IO(  0x47)
+#define DRM_IOCTL_R128_CLEAR		DRM_IOW( 0x48, drm_r128_clear_t)
+#define DRM_IOCTL_R128_VERTEX		DRM_IOW( 0x49, drm_r128_vertex_t)
+#define DRM_IOCTL_R128_INDICES		DRM_IOW( 0x4a, drm_r128_indices_t)
+#define DRM_IOCTL_R128_BLIT		DRM_IOW( 0x4b, drm_r128_blit_t)
+#define DRM_IOCTL_R128_DEPTH		DRM_IOW( 0x4c, drm_r128_depth_t)
+#define DRM_IOCTL_R128_STIPPLE		DRM_IOW( 0x4d, drm_r128_stipple_t)
+#define DRM_IOCTL_R128_PACKET		DRM_IOWR(0x4e, drm_r128_packet_t)
+
+/* Radeon specific ioctls */
+#define DRM_IOCTL_RADEON_CP_INIT	DRM_IOW( 0x40, drm_radeon_init_t)
+#define DRM_IOCTL_RADEON_CP_START	DRM_IO(  0x41)
+#define DRM_IOCTL_RADEON_CP_STOP	DRM_IOW( 0x42, drm_radeon_cp_stop_t)
+#define DRM_IOCTL_RADEON_CP_RESET	DRM_IO(  0x43)
+#define DRM_IOCTL_RADEON_CP_IDLE	DRM_IO(  0x44)
+#define DRM_IOCTL_RADEON_RESET		DRM_IO(  0x45)
+#define DRM_IOCTL_RADEON_FULLSCREEN	DRM_IOW( 0x46, drm_radeon_fullscreen_t)
+#define DRM_IOCTL_RADEON_SWAP		DRM_IO(  0x47)
+#define DRM_IOCTL_RADEON_CLEAR		DRM_IOW( 0x48, drm_radeon_clear_t)
+#define DRM_IOCTL_RADEON_VERTEX		DRM_IOW( 0x49, drm_radeon_vertex_t)
+#define DRM_IOCTL_RADEON_INDICES	DRM_IOW( 0x4a, drm_radeon_indices_t)
+#define DRM_IOCTL_RADEON_BLIT		DRM_IOW( 0x4b, drm_radeon_blit_t)
+#define DRM_IOCTL_RADEON_STIPPLE	DRM_IOW( 0x4c, drm_radeon_stipple_t)
+#define DRM_IOCTL_RADEON_INDIRECT	DRM_IOWR(0x4d, drm_radeon_indirect_t)
+
+#ifdef CONFIG_DRM40_SIS
+/* SiS specific ioctls */
+#define SIS_IOCTL_FB_ALLOC		DRM_IOWR(0x44, drm_sis_mem_t)
+#define SIS_IOCTL_FB_FREE		DRM_IOW( 0x45, drm_sis_mem_t)
+#define SIS_IOCTL_AGP_INIT		DRM_IOWR(0x53, drm_sis_agp_t)
+#define SIS_IOCTL_AGP_ALLOC		DRM_IOWR(0x54, drm_sis_mem_t)
+#define SIS_IOCTL_AGP_FREE		DRM_IOW( 0x55, drm_sis_mem_t)
+#define SIS_IOCTL_FLIP			DRM_IOW( 0x48, drm_sis_flip_t)
+#define SIS_IOCTL_FLIP_INIT		DRM_IO(  0x49)
+#define SIS_IOCTL_FLIP_FINAL		DRM_IO(  0x50)
+#endif
+
+#endif
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/drmP.h linux/drivers/char/drm-4.0/drmP.h
--- linux-2.4.18-pre2/drivers/char/drm-4.0/drmP.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/drmP.h	Thu Jan 10 13:28:33 2002
@@ -0,0 +1,836 @@
+/* drmP.h -- Private header for Direct Rendering Manager -*- linux-c -*-
+ * Created: Mon Jan  4 10:05:05 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ * 
+ */
+
+#ifndef _DRM_P_H_
+#define _DRM_P_H_
+
+#ifdef __KERNEL__
+#ifdef __alpha__
+/* add include of current.h so that "current" is defined
+ * before static inline funcs in wait.h. Doing this so we
+ * can build the DRM (part of PI DRI). 4/21/2000 S + B */
+#include <asm/current.h>
+#endif /* __alpha__ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/major.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/file.h>
+#include <linux/pci.h>
+#include <linux/wrapper.h>
+#include <linux/version.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>	/* For (un)lock_kernel */
+#include <linux/mm.h>
+#ifdef __alpha__
+#include <asm/pgtable.h> /* For pte_wrprotect */
+#endif
+#include <asm/io.h>
+#include <asm/mman.h>
+#include <asm/uaccess.h>
+#ifdef CONFIG_MTRR
+#include <asm/mtrr.h>
+#endif
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+#include <linux/types.h>
+#include <linux/agp_backend.h>
+#endif
+#if LINUX_VERSION_CODE >= 0x020100 /* KERNEL_VERSION(2,1,0) */
+#include <linux/tqueue.h>
+#include <linux/poll.h>
+#endif
+#if LINUX_VERSION_CODE < 0x020400
+#include "compat-pre24.h"
+#endif
+#include "drm.h"
+
+#define DRM_DEBUG_CODE 2	  /* Include debugging code (if > 1, then
+				     also include looping detection. */
+#define DRM_DMA_HISTOGRAM 1	  /* Make histogram of DMA latency. */
+
+#define DRM_HASH_SIZE	      16 /* Size of key hash table		  */
+#define DRM_KERNEL_CONTEXT    0	 /* Change drm_resctx if changed	  */
+#define DRM_RESERVED_CONTEXTS 1	 /* Change drm_resctx if changed	  */
+#define DRM_LOOPING_LIMIT     5000000
+#define DRM_BSZ		      1024 /* Buffer size for /dev/drm? output	  */
+#define DRM_TIME_SLICE	      (HZ/20)  /* Time slice for GLXContexts	  */
+#define DRM_LOCK_SLICE	      1	/* Time slice for lock, in jiffies	  */
+
+#define DRM_FLAG_DEBUG	  0x01
+#define DRM_FLAG_NOCTX	  0x02
+
+#define DRM_MEM_DMA	   0
+#define DRM_MEM_SAREA	   1
+#define DRM_MEM_DRIVER	   2
+#define DRM_MEM_MAGIC	   3
+#define DRM_MEM_IOCTLS	   4
+#define DRM_MEM_MAPS	   5
+#define DRM_MEM_VMAS	   6
+#define DRM_MEM_BUFS	   7
+#define DRM_MEM_SEGS	   8
+#define DRM_MEM_PAGES	   9
+#define DRM_MEM_FILES	  10
+#define DRM_MEM_QUEUES	  11
+#define DRM_MEM_CMDS	  12
+#define DRM_MEM_MAPPINGS  13
+#define DRM_MEM_BUFLISTS  14
+#define DRM_MEM_AGPLISTS  15
+#define DRM_MEM_TOTALAGP  16
+#define DRM_MEM_BOUNDAGP  17
+#define DRM_MEM_CTXBITMAP 18
+
+#define DRM_MAX_CTXBITMAP (PAGE_SIZE * 8)
+
+				/* Backward compatibility section */
+				/* _PAGE_WT changed to _PAGE_PWT in 2.2.6 */
+#ifndef _PAGE_PWT
+#define _PAGE_PWT _PAGE_WT
+#endif
+				/* Wait queue declarations changed in 2.3.1 */
+#ifndef DECLARE_WAITQUEUE
+#define DECLARE_WAITQUEUE(w,c) struct wait_queue w = { c, NULL }
+typedef struct wait_queue *wait_queue_head_t;
+#define init_waitqueue_head(q) *q = NULL;
+#endif
+
+				/* _PAGE_4M changed to _PAGE_PSE in 2.3.23 */
+#ifndef _PAGE_PSE
+#define _PAGE_PSE _PAGE_4M
+#endif
+
+				/* vm_offset changed to vm_pgoff in 2.3.25 */
+#if LINUX_VERSION_CODE < 0x020319
+#define VM_OFFSET(vma) ((vma)->vm_offset)
+#else
+#define VM_OFFSET(vma) ((vma)->vm_pgoff << PAGE_SHIFT)
+#endif
+
+				/* *_nopage return values defined in 2.3.26 */
+#ifndef NOPAGE_SIGBUS
+#define NOPAGE_SIGBUS 0
+#endif
+#ifndef NOPAGE_OOM
+#define NOPAGE_OOM 0
+#endif
+
+				/* module_init/module_exit added in 2.3.13 */
+#ifndef module_init
+#define module_init(x)  int init_module(void) { return x(); }
+#endif
+#ifndef module_exit
+#define module_exit(x)  void cleanup_module(void) { x(); }
+#endif
+
+				/* Generic cmpxchg added in 2.3.x */
+#ifndef __HAVE_ARCH_CMPXCHG
+				/* Include this here so that driver can be
+                                   used with older kernels. */
+#if defined(__alpha__)
+static __inline__ unsigned long
+__cmpxchg_u32(volatile int *m, int old, int new)
+{
+	unsigned long prev, cmp;
+
+	__asm__ __volatile__(
+	"1:	ldl_l %0,%2\n"
+	"	cmpeq %0,%3,%1\n"
+	"	beq %1,2f\n"
+	"	mov %4,%1\n"
+	"	stl_c %1,%2\n"
+	"	beq %1,3f\n"
+	"2:	mb\n"
+	".subsection 2\n"
+	"3:	br 1b\n"
+	".previous"
+	: "=&r"(prev), "=&r"(cmp), "=m"(*m)
+	: "r"((long) old), "r"(new), "m"(*m));
+
+	return prev;
+}
+
+static __inline__ unsigned long
+__cmpxchg_u64(volatile long *m, unsigned long old, unsigned long new)
+{
+	unsigned long prev, cmp;
+
+	__asm__ __volatile__(
+	"1:	ldq_l %0,%2\n"
+	"	cmpeq %0,%3,%1\n"
+	"	beq %1,2f\n"
+	"	mov %4,%1\n"
+	"	stq_c %1,%2\n"
+	"	beq %1,3f\n"
+	"2:	mb\n"
+	".subsection 2\n"
+	"3:	br 1b\n"
+	".previous"
+	: "=&r"(prev), "=&r"(cmp), "=m"(*m)
+	: "r"((long) old), "r"(new), "m"(*m));
+
+	return prev;
+}
+
+static __inline__ unsigned long
+__cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size)
+{
+	switch (size) {
+		case 4:
+			return __cmpxchg_u32(ptr, old, new);
+		case 8:
+			return __cmpxchg_u64(ptr, old, new);
+	}
+	return old;
+}
+#define cmpxchg(ptr,o,n)						 \
+  ({									 \
+     __typeof__(*(ptr)) _o_ = (o);					 \
+     __typeof__(*(ptr)) _n_ = (n);					 \
+     (__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_,		 \
+				    (unsigned long)_n_, sizeof(*(ptr))); \
+  })
+
+#elif __i386__
+static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
+				      unsigned long new, int size)
+{
+	unsigned long prev;
+	switch (size) {
+	case 1:
+		__asm__ __volatile__(LOCK_PREFIX "cmpxchgb %b1,%2"
+				     : "=a"(prev)
+				     : "q"(new), "m"(*__xg(ptr)), "0"(old)
+				     : "memory");
+		return prev;
+	case 2:
+		__asm__ __volatile__(LOCK_PREFIX "cmpxchgw %w1,%2"
+				     : "=a"(prev)
+				     : "q"(new), "m"(*__xg(ptr)), "0"(old)
+				     : "memory");
+		return prev;
+	case 4:
+		__asm__ __volatile__(LOCK_PREFIX "cmpxchgl %1,%2"
+				     : "=a"(prev)
+				     : "q"(new), "m"(*__xg(ptr)), "0"(old)
+				     : "memory");
+		return prev;
+	}
+	return old;
+}
+
+#define cmpxchg(ptr,o,n)						\
+  ((__typeof__(*(ptr)))__cmpxchg((ptr),(unsigned long)(o),		\
+				 (unsigned long)(n),sizeof(*(ptr))))
+#endif /* i386 & alpha */
+#endif
+
+				/* Macros to make printk easier */
+#define DRM_ERROR(fmt, arg...) \
+	printk(KERN_ERR "[" DRM_NAME ":" __FUNCTION__ "] *ERROR* " fmt , ##arg)
+#define DRM_MEM_ERROR(area, fmt, arg...) \
+	printk(KERN_ERR "[" DRM_NAME ":" __FUNCTION__ ":%s] *ERROR* " fmt , \
+	       drm_mem_stats[area].name , ##arg)
+#define DRM_INFO(fmt, arg...)  printk(KERN_INFO "[" DRM_NAME "] " fmt , ##arg)
+
+#if DRM_DEBUG_CODE
+#define DRM_DEBUG(fmt, arg...)						  \
+	do {								  \
+		if (drm_flags&DRM_FLAG_DEBUG)				  \
+			printk(KERN_DEBUG				  \
+			       "[" DRM_NAME ":" __FUNCTION__ "] " fmt ,	  \
+			       ##arg);					  \
+	} while (0)
+#else
+#define DRM_DEBUG(fmt, arg...)		 do { } while (0)
+#endif
+
+#define DRM_PROC_LIMIT (PAGE_SIZE-80)
+
+#define DRM_PROC_PRINT(fmt, arg...)	   \
+   len += sprintf(&buf[len], fmt , ##arg); \
+   if (len > DRM_PROC_LIMIT) return len;
+
+#define DRM_PROC_PRINT_RET(ret, fmt, arg...)	    \
+   len += sprintf(&buf[len], fmt , ##arg);	    \
+   if (len > DRM_PROC_LIMIT) { ret; return len; }
+
+				/* Internal types and structures */
+#define DRM_ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+#define DRM_MIN(a,b) ((a)<(b)?(a):(b))
+#define DRM_MAX(a,b) ((a)>(b)?(a):(b))
+
+#define DRM_LEFTCOUNT(x) (((x)->rp + (x)->count - (x)->wp) % ((x)->count + 1))
+#define DRM_BUFCOUNT(x) ((x)->count - DRM_LEFTCOUNT(x))
+#define DRM_WAITCOUNT(dev,idx) DRM_BUFCOUNT(&dev->queuelist[idx]->waitlist)
+
+typedef int drm_ioctl_t(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+
+typedef struct drm_ioctl_desc {
+	drm_ioctl_t	     *func;
+	int		     auth_needed;
+	int		     root_only;
+} drm_ioctl_desc_t;
+
+typedef struct drm_devstate {
+	pid_t		  owner;	/* X server pid holding x_lock */
+	
+} drm_devstate_t;
+
+typedef struct drm_magic_entry {
+	drm_magic_t	       magic;
+	struct drm_file	       *priv;
+	struct drm_magic_entry *next;
+} drm_magic_entry_t;
+
+typedef struct drm_magic_head {
+	struct drm_magic_entry *head;
+	struct drm_magic_entry *tail;
+} drm_magic_head_t;
+
+typedef struct drm_vma_entry {
+	struct vm_area_struct *vma;
+	struct drm_vma_entry  *next;
+	pid_t		      pid;
+} drm_vma_entry_t;
+
+typedef struct drm_buf {
+	int		  idx;	       /* Index into master buflist	     */
+	int		  total;       /* Buffer size			     */
+	int		  order;       /* log-base-2(total)		     */
+	int		  used;	       /* Amount of buffer in use (for DMA)  */
+	unsigned long	  offset;      /* Byte offset (used internally)	     */
+	void		  *address;    /* Address of buffer		     */
+	unsigned long	  bus_address; /* Bus address of buffer		     */
+	struct drm_buf	  *next;       /* Kernel-only: used for free list    */
+	__volatile__ int  waiting;     /* On kernel DMA queue		     */
+	__volatile__ int  pending;     /* On hardware DMA queue		     */
+	wait_queue_head_t dma_wait;    /* Processes waiting		     */
+	pid_t		  pid;	       /* PID of holding process	     */
+	int		  context;     /* Kernel queue for this buffer	     */
+	int		  while_locked;/* Dispatch this buffer while locked  */
+	enum {
+		DRM_LIST_NONE	 = 0,
+		DRM_LIST_FREE	 = 1,
+		DRM_LIST_WAIT	 = 2,
+		DRM_LIST_PEND	 = 3,
+		DRM_LIST_PRIO	 = 4,
+		DRM_LIST_RECLAIM = 5
+	}		  list;	       /* Which list we're on		     */
+
+#if DRM_DMA_HISTOGRAM
+	cycles_t	  time_queued;	   /* Queued to kernel DMA queue     */
+	cycles_t	  time_dispatched; /* Dispatched to hardware	     */
+	cycles_t	  time_completed;  /* Completed by hardware	     */
+	cycles_t	  time_freed;	   /* Back on freelist		     */
+#endif
+
+	int		  dev_priv_size; /* Size of buffer private stoarge   */
+	void		  *dev_private;  /* Per-buffer private storage       */
+} drm_buf_t;
+
+#if DRM_DMA_HISTOGRAM
+#define DRM_DMA_HISTOGRAM_SLOTS		  9
+#define DRM_DMA_HISTOGRAM_INITIAL	 10
+#define DRM_DMA_HISTOGRAM_NEXT(current)	 ((current)*10)
+typedef struct drm_histogram {
+	atomic_t	  total;
+	
+	atomic_t	  queued_to_dispatched[DRM_DMA_HISTOGRAM_SLOTS];
+	atomic_t	  dispatched_to_completed[DRM_DMA_HISTOGRAM_SLOTS];
+	atomic_t	  completed_to_freed[DRM_DMA_HISTOGRAM_SLOTS];
+	
+	atomic_t	  queued_to_completed[DRM_DMA_HISTOGRAM_SLOTS];
+	atomic_t	  queued_to_freed[DRM_DMA_HISTOGRAM_SLOTS];
+	
+	atomic_t	  dma[DRM_DMA_HISTOGRAM_SLOTS];
+	atomic_t	  schedule[DRM_DMA_HISTOGRAM_SLOTS];
+	atomic_t	  ctx[DRM_DMA_HISTOGRAM_SLOTS];
+	atomic_t	  lacq[DRM_DMA_HISTOGRAM_SLOTS];
+	atomic_t	  lhld[DRM_DMA_HISTOGRAM_SLOTS];
+} drm_histogram_t;
+#endif
+
+				/* bufs is one longer than it has to be */
+typedef struct drm_waitlist {
+	int		  count;	/* Number of possible buffers	   */
+	drm_buf_t	  **bufs;	/* List of pointers to buffers	   */
+	drm_buf_t	  **rp;		/* Read pointer			   */
+	drm_buf_t	  **wp;		/* Write pointer		   */
+	drm_buf_t	  **end;	/* End pointer			   */
+	spinlock_t	  read_lock;
+	spinlock_t	  write_lock;
+} drm_waitlist_t;
+
+typedef struct drm_freelist {
+	int		  initialized; /* Freelist in use		   */
+	atomic_t	  count;       /* Number of free buffers	   */
+	drm_buf_t	  *next;       /* End pointer			   */
+	
+	wait_queue_head_t waiting;     /* Processes waiting on free bufs   */
+	int		  low_mark;    /* Low water mark		   */
+	int		  high_mark;   /* High water mark		   */
+	atomic_t	  wfh;	       /* If waiting for high mark	   */
+	spinlock_t        lock;
+} drm_freelist_t;
+
+typedef struct drm_buf_entry {
+	int		  buf_size;
+	int		  buf_count;
+	drm_buf_t	  *buflist;
+	int		  seg_count;
+	int		  page_order;
+	unsigned long	  *seglist;
+
+	drm_freelist_t	  freelist;
+} drm_buf_entry_t;
+
+typedef struct drm_hw_lock {
+	__volatile__ unsigned int lock;
+	char			  padding[60]; /* Pad to cache line */
+} drm_hw_lock_t;
+
+typedef struct drm_file {
+	int		  authenticated;
+	int		  minor;
+	pid_t		  pid;
+	uid_t		  uid;
+	drm_magic_t	  magic;
+	unsigned long	  ioctl_count;
+	struct drm_file	  *next;
+	struct drm_file	  *prev;
+	struct drm_device *dev;
+	int 		  remove_auth_on_close;
+} drm_file_t;
+
+
+typedef struct drm_queue {
+	atomic_t	  use_count;	/* Outstanding uses (+1)	    */
+	atomic_t	  finalization;	/* Finalization in progress	    */
+	atomic_t	  block_count;	/* Count of processes waiting	    */
+	atomic_t	  block_read;	/* Queue blocked for reads	    */
+	wait_queue_head_t read_queue;	/* Processes waiting on block_read  */
+	atomic_t	  block_write;	/* Queue blocked for writes	    */
+	wait_queue_head_t write_queue;	/* Processes waiting on block_write */
+	atomic_t	  total_queued;	/* Total queued statistic	    */
+	atomic_t	  total_flushed;/* Total flushes statistic	    */
+	atomic_t	  total_locks;	/* Total locks statistics	    */
+	drm_ctx_flags_t	  flags;	/* Context preserving and 2D-only   */
+	drm_waitlist_t	  waitlist;	/* Pending buffers		    */
+	wait_queue_head_t flush_queue;	/* Processes waiting until flush    */
+} drm_queue_t;
+
+typedef struct drm_lock_data {
+	drm_hw_lock_t	  *hw_lock;	/* Hardware lock		   */
+	pid_t		  pid;		/* PID of lock holder (0=kernel)   */
+	wait_queue_head_t lock_queue;	/* Queue of blocked processes	   */
+	unsigned long	  lock_time;	/* Time of last lock in jiffies	   */
+} drm_lock_data_t;
+
+typedef struct drm_device_dma {
+				/* Performance Counters */
+	atomic_t	  total_prio;	/* Total DRM_DMA_PRIORITY	   */
+	atomic_t	  total_bytes;	/* Total bytes DMA'd		   */
+	atomic_t	  total_dmas;	/* Total DMA buffers dispatched	   */
+	
+	atomic_t	  total_missed_dma;  /* Missed drm_do_dma	    */
+	atomic_t	  total_missed_lock; /* Missed lock in drm_do_dma   */
+	atomic_t	  total_missed_free; /* Missed drm_free_this_buffer */
+	atomic_t	  total_missed_sched;/* Missed drm_dma_schedule	    */
+
+	atomic_t	  total_tried;	/* Tried next_buffer		    */
+	atomic_t	  total_hit;	/* Sent next_buffer		    */
+	atomic_t	  total_lost;	/* Lost interrupt		    */
+
+	drm_buf_entry_t	  bufs[DRM_MAX_ORDER+1];
+	int		  buf_count;
+	drm_buf_t	  **buflist;	/* Vector of pointers info bufs	   */
+	int		  seg_count; 
+	int		  page_count;
+	unsigned long	  *pagelist;
+	unsigned long	  byte_count;
+	enum {
+	   _DRM_DMA_USE_AGP = 0x01
+	} flags;
+
+				/* DMA support */
+	drm_buf_t	  *this_buffer;	/* Buffer being sent		   */
+	drm_buf_t	  *next_buffer; /* Selected buffer to send	   */
+	drm_queue_t	  *next_queue;	/* Queue from which buffer selected*/
+	wait_queue_head_t waiting;	/* Processes waiting on free bufs  */
+} drm_device_dma_t;
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+typedef struct drm_agp_mem {
+	unsigned long      handle;
+	agp_memory         *memory;
+	unsigned long      bound; /* address */
+	int                pages;
+	struct drm_agp_mem *prev;
+	struct drm_agp_mem *next;
+} drm_agp_mem_t;
+
+typedef struct drm_agp_head {
+	agp_kern_info      agp_info;
+	const char         *chipset;
+	drm_agp_mem_t      *memory;
+	unsigned long      mode;
+	int                enabled;
+	int                acquired;
+	unsigned long      base;
+   	int 		   agp_mtrr;
+} drm_agp_head_t;
+#endif
+
+typedef struct drm_sigdata {
+	int           context;
+	drm_hw_lock_t *lock;
+} drm_sigdata_t;
+
+typedef struct drm_device {
+	const char	  *name;	/* Simple driver name		   */
+	char		  *unique;	/* Unique identifier: e.g., busid  */
+	int		  unique_len;	/* Length of unique field	   */
+	dev_t		  device;	/* Device number for mknod	   */
+	char		  *devname;	/* For /proc/interrupts		   */
+	
+	int		  blocked;	/* Blocked due to VC switch?	   */
+	struct proc_dir_entry *root;	/* Root for this device's entries  */
+
+				/* Locks */
+	spinlock_t	  count_lock;	/* For inuse, open_count, buf_use  */
+	struct semaphore  struct_sem;	/* For others			   */
+
+				/* Usage Counters */
+	int		  open_count;	/* Outstanding files open	   */
+	atomic_t	  ioctl_count;	/* Outstanding IOCTLs pending	   */
+	atomic_t	  vma_count;	/* Outstanding vma areas open	   */
+	int		  buf_use;	/* Buffers in use -- cannot alloc  */
+	atomic_t	  buf_alloc;	/* Buffer allocation in progress   */
+
+				/* Performance Counters */
+	atomic_t	  total_open;
+	atomic_t	  total_close;
+	atomic_t	  total_ioctl;
+	atomic_t	  total_irq;	/* Total interruptions		   */
+	atomic_t	  total_ctx;	/* Total context switches	   */
+	
+	atomic_t	  total_locks;
+	atomic_t	  total_unlocks;
+	atomic_t	  total_contends;
+	atomic_t	  total_sleeps;
+
+				/* Authentication */
+	drm_file_t	  *file_first;
+	drm_file_t	  *file_last;
+	drm_magic_head_t  magiclist[DRM_HASH_SIZE];
+
+				/* Memory management */
+	drm_map_t	  **maplist;	/* Vector of pointers to regions   */
+	int		  map_count;	/* Number of mappable regions	   */
+
+	drm_vma_entry_t	  *vmalist;	/* List of vmas (for debugging)	   */
+	drm_lock_data_t	  lock;		/* Information on hardware lock	   */
+
+				/* DMA queues (contexts) */
+	int		  queue_count;	/* Number of active DMA queues	   */
+	int		  queue_reserved; /* Number of reserved DMA queues */
+	int		  queue_slots;	/* Actual length of queuelist	   */
+	drm_queue_t	  **queuelist;	/* Vector of pointers to DMA queues */
+	drm_device_dma_t  *dma;		/* Optional pointer for DMA support */
+
+				/* Context support */
+	int		  irq;		/* Interrupt used by board	   */
+	__volatile__ long context_flag;	/* Context swapping flag	   */
+	__volatile__ long interrupt_flag; /* Interruption handler flag	   */
+	__volatile__ long dma_flag;	/* DMA dispatch flag		   */
+	struct timer_list timer;	/* Timer for delaying ctx switch   */
+	wait_queue_head_t context_wait; /* Processes waiting on ctx switch */
+	int		  last_checked;	/* Last context checked for DMA	   */
+	int		  last_context;	/* Last current context		   */
+	unsigned long	  last_switch;	/* jiffies at last context switch  */
+	struct tq_struct  tq;
+	cycles_t	  ctx_start;
+	cycles_t	  lck_start;
+#if DRM_DMA_HISTOGRAM
+	drm_histogram_t	  histo;
+#endif
+	
+				/* Callback to X server for context switch
+				   and for heavy-handed reset. */
+	char		  buf[DRM_BSZ]; /* Output buffer		   */
+	char		  *buf_rp;	/* Read pointer			   */
+	char		  *buf_wp;	/* Write pointer		   */
+	char		  *buf_end;	/* End pointer			   */
+	struct fasync_struct *buf_async;/* Processes waiting for SIGIO	   */
+	wait_queue_head_t buf_readers;	/* Processes waiting to read	   */
+	wait_queue_head_t buf_writers;	/* Processes waiting to ctx switch */
+	
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	drm_agp_head_t    *agp;
+#endif
+	unsigned long     *ctx_bitmap;
+	void		  *dev_private;
+	drm_sigdata_t     sigdata; /* For block_all_signals */
+	sigset_t          sigmask;
+} drm_device_t;
+
+
+				/* Internal function definitions */
+
+				/* Misc. support (init.c) */
+extern int	     drm_flags;
+extern void	     drm_parse_options(char *s);
+extern int           drm_cpu_valid(void);
+
+
+				/* Device support (fops.c) */
+extern int	     drm_open_helper(struct inode *inode, struct file *filp,
+				     drm_device_t *dev);
+extern int	     drm_flush(struct file *filp);
+extern int	     drm_release(struct inode *inode, struct file *filp);
+extern int	     drm_fasync(int fd, struct file *filp, int on);
+extern ssize_t	     drm_read(struct file *filp, char *buf, size_t count,
+			      loff_t *off);
+extern int	     drm_write_string(drm_device_t *dev, const char *s);
+extern unsigned int  drm_poll(struct file *filp, struct poll_table_struct *wait);
+
+				/* Mapping support (vm.c) */
+#if LINUX_VERSION_CODE < 0x020317
+extern unsigned long drm_vm_nopage(struct vm_area_struct *vma,
+				   unsigned long address,
+				   int write_access);
+extern unsigned long drm_vm_shm_nopage(struct vm_area_struct *vma,
+				       unsigned long address,
+				       int write_access);
+extern unsigned long drm_vm_shm_nopage_lock(struct vm_area_struct *vma,
+					    unsigned long address,
+					    int write_access);
+extern unsigned long drm_vm_dma_nopage(struct vm_area_struct *vma,
+				       unsigned long address,
+				       int write_access);
+#else
+				/* Return type changed in 2.3.23 */
+extern struct page *drm_vm_nopage(struct vm_area_struct *vma,
+				  unsigned long address,
+				  int write_access);
+extern struct page *drm_vm_shm_nopage(struct vm_area_struct *vma,
+				      unsigned long address,
+				      int write_access);
+extern struct page *drm_vm_shm_nopage_lock(struct vm_area_struct *vma,
+					   unsigned long address,
+					   int write_access);
+extern struct page *drm_vm_dma_nopage(struct vm_area_struct *vma,
+				      unsigned long address,
+				      int write_access);
+#endif
+extern void	     drm_vm_open(struct vm_area_struct *vma);
+extern void	     drm_vm_close(struct vm_area_struct *vma);
+extern int	     drm_mmap_dma(struct file *filp,
+				  struct vm_area_struct *vma);
+extern int	     drm_mmap(struct file *filp, struct vm_area_struct *vma);
+
+
+				/* Proc support (proc.c) */
+extern int	     drm_proc_init(drm_device_t *dev);
+extern int	     drm_proc_cleanup(void);
+
+				/* Memory management support (memory.c) */
+extern void	     drm_mem_init(void);
+extern int	     drm_mem_info(char *buf, char **start, off_t offset,
+				  int len, int *eof, void *data);
+extern void	     *drm_alloc(size_t size, int area);
+extern void	     *drm_realloc(void *oldpt, size_t oldsize, size_t size,
+				  int area);
+extern char	     *drm_strdup(const char *s, int area);
+extern void	     drm_strfree(const char *s, int area);
+extern void	     drm_free(void *pt, size_t size, int area);
+extern unsigned long drm_alloc_pages(int order, int area);
+extern void	     drm_free_pages(unsigned long address, int order,
+				    int area);
+extern void	     *drm_ioremap(unsigned long offset, unsigned long size);
+extern void	     drm_ioremapfree(void *pt, unsigned long size);
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+extern agp_memory    *drm_alloc_agp(int pages, u32 type);
+extern int           drm_free_agp(agp_memory *handle, int pages);
+extern int           drm_bind_agp(agp_memory *handle, unsigned int start);
+extern int           drm_unbind_agp(agp_memory *handle);
+#endif
+
+
+				/* Buffer management support (bufs.c) */
+extern int	     drm_order(unsigned long size);
+extern int	     drm_addmap(struct inode *inode, struct file *filp,
+				unsigned int cmd, unsigned long arg);
+extern int	     drm_addbufs(struct inode *inode, struct file *filp,
+				 unsigned int cmd, unsigned long arg);
+extern int	     drm_infobufs(struct inode *inode, struct file *filp,
+				  unsigned int cmd, unsigned long arg);
+extern int	     drm_markbufs(struct inode *inode, struct file *filp,
+				  unsigned int cmd, unsigned long arg);
+extern int	     drm_freebufs(struct inode *inode, struct file *filp,
+				  unsigned int cmd, unsigned long arg);
+extern int	     drm_mapbufs(struct inode *inode, struct file *filp,
+				 unsigned int cmd, unsigned long arg);
+
+
+				/* Buffer list management support (lists.c) */
+extern int	     drm_waitlist_create(drm_waitlist_t *bl, int count);
+extern int	     drm_waitlist_destroy(drm_waitlist_t *bl);
+extern int	     drm_waitlist_put(drm_waitlist_t *bl, drm_buf_t *buf);
+extern drm_buf_t     *drm_waitlist_get(drm_waitlist_t *bl);
+
+extern int	     drm_freelist_create(drm_freelist_t *bl, int count);
+extern int	     drm_freelist_destroy(drm_freelist_t *bl);
+extern int	     drm_freelist_put(drm_device_t *dev, drm_freelist_t *bl,
+				      drm_buf_t *buf);
+extern drm_buf_t     *drm_freelist_get(drm_freelist_t *bl, int block);
+
+				/* DMA support (gen_dma.c) */
+extern void	     drm_dma_setup(drm_device_t *dev);
+extern void	     drm_dma_takedown(drm_device_t *dev);
+extern void	     drm_free_buffer(drm_device_t *dev, drm_buf_t *buf);
+extern void	     drm_reclaim_buffers(drm_device_t *dev, pid_t pid);
+extern int	     drm_context_switch(drm_device_t *dev, int old, int new);
+extern int	     drm_context_switch_complete(drm_device_t *dev, int new);
+extern void	     drm_clear_next_buffer(drm_device_t *dev);
+extern int	     drm_select_queue(drm_device_t *dev,
+				      void (*wrapper)(unsigned long));
+extern int	     drm_dma_enqueue(drm_device_t *dev, drm_dma_t *dma);
+extern int	     drm_dma_get_buffers(drm_device_t *dev, drm_dma_t *dma);
+#if DRM_DMA_HISTOGRAM
+extern int	     drm_histogram_slot(unsigned long count);
+extern void	     drm_histogram_compute(drm_device_t *dev, drm_buf_t *buf);
+#endif
+
+
+				/* Misc. IOCTL support (ioctl.c) */
+extern int	     drm_irq_busid(struct inode *inode, struct file *filp,
+				   unsigned int cmd, unsigned long arg);
+extern int	     drm_getunique(struct inode *inode, struct file *filp,
+				   unsigned int cmd, unsigned long arg);
+extern int	     drm_setunique(struct inode *inode, struct file *filp,
+				   unsigned int cmd, unsigned long arg);
+
+
+				/* Context IOCTL support (context.c) */
+extern int	     drm_resctx(struct inode *inode, struct file *filp,
+				unsigned int cmd, unsigned long arg);
+extern int	     drm_addctx(struct inode *inode, struct file *filp,
+				unsigned int cmd, unsigned long arg);
+extern int	     drm_modctx(struct inode *inode, struct file *filp,
+				unsigned int cmd, unsigned long arg);
+extern int	     drm_getctx(struct inode *inode, struct file *filp,
+				unsigned int cmd, unsigned long arg);
+extern int	     drm_switchctx(struct inode *inode, struct file *filp,
+				   unsigned int cmd, unsigned long arg);
+extern int	     drm_newctx(struct inode *inode, struct file *filp,
+				unsigned int cmd, unsigned long arg);
+extern int	     drm_rmctx(struct inode *inode, struct file *filp,
+			       unsigned int cmd, unsigned long arg);
+
+
+				/* Drawable IOCTL support (drawable.c) */
+extern int	     drm_adddraw(struct inode *inode, struct file *filp,
+				 unsigned int cmd, unsigned long arg);
+extern int	     drm_rmdraw(struct inode *inode, struct file *filp,
+				unsigned int cmd, unsigned long arg);
+
+
+				/* Authentication IOCTL support (auth.c) */
+extern int	     drm_add_magic(drm_device_t *dev, drm_file_t *priv,
+				   drm_magic_t magic);
+extern int	     drm_remove_magic(drm_device_t *dev, drm_magic_t magic);
+extern int	     drm_getmagic(struct inode *inode, struct file *filp,
+				  unsigned int cmd, unsigned long arg);
+extern int	     drm_authmagic(struct inode *inode, struct file *filp,
+				   unsigned int cmd, unsigned long arg);
+
+
+				/* Locking IOCTL support (lock.c) */
+extern int	     drm_block(struct inode *inode, struct file *filp,
+			       unsigned int cmd, unsigned long arg);
+extern int	     drm_unblock(struct inode *inode, struct file *filp,
+				 unsigned int cmd, unsigned long arg);
+extern int	     drm_lock_take(__volatile__ unsigned int *lock,
+				   unsigned int context);
+extern int	     drm_lock_transfer(drm_device_t *dev,
+				       __volatile__ unsigned int *lock,
+				       unsigned int context);
+extern int	     drm_lock_free(drm_device_t *dev,
+				   __volatile__ unsigned int *lock,
+				   unsigned int context);
+extern int	     drm_finish(struct inode *inode, struct file *filp,
+				unsigned int cmd, unsigned long arg);
+extern int	     drm_flush_unblock(drm_device_t *dev, int context,
+				       drm_lock_flags_t flags);
+extern int	     drm_flush_block_and_flush(drm_device_t *dev, int context,
+					       drm_lock_flags_t flags);
+extern int           drm_notifier(void *priv);
+
+				/* Context Bitmap support (ctxbitmap.c) */
+extern int	     drm_ctxbitmap_init(drm_device_t *dev);
+extern void	     drm_ctxbitmap_cleanup(drm_device_t *dev);
+extern int	     drm_ctxbitmap_next(drm_device_t *dev);
+extern void	     drm_ctxbitmap_free(drm_device_t *dev, int ctx_handle);
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+				/* AGP/GART support (agpsupport.c) */
+extern drm_agp_head_t *drm_agp_init(void);
+extern void           drm_agp_uninit(void);
+extern int            drm_agp_acquire(struct inode *inode, struct file *filp,
+				      unsigned int cmd, unsigned long arg);
+extern void           _drm_agp_release(void);
+extern int            drm_agp_release(struct inode *inode, struct file *filp,
+				      unsigned int cmd, unsigned long arg);
+extern int            drm_agp_enable(struct inode *inode, struct file *filp,
+				     unsigned int cmd, unsigned long arg);
+extern int            drm_agp_info(struct inode *inode, struct file *filp,
+				   unsigned int cmd, unsigned long arg);
+extern int            drm_agp_alloc(struct inode *inode, struct file *filp,
+				    unsigned int cmd, unsigned long arg);
+extern int            drm_agp_free(struct inode *inode, struct file *filp,
+				   unsigned int cmd, unsigned long arg);
+extern int            drm_agp_unbind(struct inode *inode, struct file *filp,
+				     unsigned int cmd, unsigned long arg);
+extern int            drm_agp_bind(struct inode *inode, struct file *filp,
+				   unsigned int cmd, unsigned long arg);
+extern agp_memory     *drm_agp_allocate_memory(size_t pages, u32 type);
+extern int            drm_agp_free_memory(agp_memory *handle);
+extern int            drm_agp_bind_memory(agp_memory *handle, off_t start);
+extern int            drm_agp_unbind_memory(agp_memory *handle);
+#endif
+#endif
+#endif
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/ffb_context.c linux/drivers/char/drm-4.0/ffb_context.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/ffb_context.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/ffb_context.c	Thu Jan 10 13:28:33 2002
@@ -0,0 +1,540 @@
+/* $Id: ffb_context.c,v 1.1 2002/01/09 21:56:59 marcelo Exp $
+ * ffb_context.c: Creator/Creator3D DRI/DRM context switching.
+ *
+ * Copyright (C) 2000 David S. Miller (davem@redhat.com)
+ *
+ * Almost entirely stolen from tdfx_context.c, see there
+ * for authors.
+ */
+
+#include <linux/sched.h>
+#include <asm/upa.h>
+
+#include "drmP.h"
+
+#include "ffb_drv.h"
+
+static int ffb_alloc_queue(drm_device_t *dev, int is_2d_only)
+{
+	ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) (dev + 1);
+	int i;
+
+	for (i = 0; i < FFB_MAX_CTXS; i++) {
+		if (fpriv->hw_state[i] == NULL)
+			break;
+	}
+	if (i == FFB_MAX_CTXS)
+		return -1;
+
+	fpriv->hw_state[i] = kmalloc(sizeof(struct ffb_hw_context), GFP_KERNEL);
+	if (fpriv->hw_state[i] == NULL)
+		return -1;
+
+	fpriv->hw_state[i]->is_2d_only = is_2d_only;
+
+	/* Plus one because 0 is the special DRM_KERNEL_CONTEXT. */
+	return i + 1;
+}
+
+static void ffb_save_context(ffb_dev_priv_t *fpriv, int idx)
+{
+	ffb_fbcPtr ffb = fpriv->regs;
+	struct ffb_hw_context *ctx;
+	int i;
+
+	ctx = fpriv->hw_state[idx - 1];
+	if (idx == 0 || ctx == NULL)
+		return;
+
+	if (ctx->is_2d_only) {
+		/* 2D applications only care about certain pieces
+		 * of state.
+		 */
+		ctx->drawop = upa_readl(&ffb->drawop);
+		ctx->ppc = upa_readl(&ffb->ppc);
+		ctx->wid = upa_readl(&ffb->wid);
+		ctx->fg = upa_readl(&ffb->fg);
+		ctx->bg = upa_readl(&ffb->bg);
+		ctx->xclip = upa_readl(&ffb->xclip);
+		ctx->fbc = upa_readl(&ffb->fbc);
+		ctx->rop = upa_readl(&ffb->rop);
+		ctx->cmp = upa_readl(&ffb->cmp);
+		ctx->matchab = upa_readl(&ffb->matchab);
+		ctx->magnab = upa_readl(&ffb->magnab);
+		ctx->pmask = upa_readl(&ffb->pmask);
+		ctx->xpmask = upa_readl(&ffb->xpmask);
+		ctx->lpat = upa_readl(&ffb->lpat);
+		ctx->fontxy = upa_readl(&ffb->fontxy);
+		ctx->fontw = upa_readl(&ffb->fontw);
+		ctx->fontinc = upa_readl(&ffb->fontinc);
+
+		/* stencil/stencilctl only exists on FFB2+ and later
+		 * due to the introduction of 3DRAM-III.
+		 */
+		if (fpriv->ffb_type == ffb2_vertical_plus ||
+		    fpriv->ffb_type == ffb2_horizontal_plus) {
+			ctx->stencil = upa_readl(&ffb->stencil);
+			ctx->stencilctl = upa_readl(&ffb->stencilctl);
+		}
+
+		for (i = 0; i < 32; i++)
+			ctx->area_pattern[i] = upa_readl(&ffb->pattern[i]);
+		ctx->ucsr = upa_readl(&ffb->ucsr);
+		return;
+	}
+
+	/* Fetch drawop. */
+	ctx->drawop = upa_readl(&ffb->drawop);
+
+	/* If we were saving the vertex registers, this is where
+	 * we would do it.  We would save 32 32-bit words starting
+	 * at ffb->suvtx.
+	 */
+
+	/* Capture rendering attributes. */
+
+	ctx->ppc = upa_readl(&ffb->ppc);		/* Pixel Processor Control */
+	ctx->wid = upa_readl(&ffb->wid);		/* Current WID */
+	ctx->fg = upa_readl(&ffb->fg);			/* Constant FG color */
+	ctx->bg = upa_readl(&ffb->bg);			/* Constant BG color */
+	ctx->consty = upa_readl(&ffb->consty);		/* Constant Y */
+	ctx->constz = upa_readl(&ffb->constz);		/* Constant Z */
+	ctx->xclip = upa_readl(&ffb->xclip);		/* X plane clip */
+	ctx->dcss = upa_readl(&ffb->dcss);		/* Depth Cue Scale Slope */
+	ctx->vclipmin = upa_readl(&ffb->vclipmin);	/* Primary XY clip, minimum */
+	ctx->vclipmax = upa_readl(&ffb->vclipmax);	/* Primary XY clip, maximum */
+	ctx->vclipzmin = upa_readl(&ffb->vclipzmin);	/* Primary Z clip, minimum */
+	ctx->vclipzmax = upa_readl(&ffb->vclipzmax);	/* Primary Z clip, maximum */
+	ctx->dcsf = upa_readl(&ffb->dcsf);		/* Depth Cue Scale Front Bound */
+	ctx->dcsb = upa_readl(&ffb->dcsb);		/* Depth Cue Scale Back Bound */
+	ctx->dczf = upa_readl(&ffb->dczf);		/* Depth Cue Scale Z Front */
+	ctx->dczb = upa_readl(&ffb->dczb);		/* Depth Cue Scale Z Back */
+	ctx->blendc = upa_readl(&ffb->blendc);		/* Alpha Blend Control */
+	ctx->blendc1 = upa_readl(&ffb->blendc1);	/* Alpha Blend Color 1 */
+	ctx->blendc2 = upa_readl(&ffb->blendc2);	/* Alpha Blend Color 2 */
+	ctx->fbc = upa_readl(&ffb->fbc);		/* Frame Buffer Control */
+	ctx->rop = upa_readl(&ffb->rop);		/* Raster Operation */
+	ctx->cmp = upa_readl(&ffb->cmp);		/* Compare Controls */
+	ctx->matchab = upa_readl(&ffb->matchab);	/* Buffer A/B Match Ops */
+	ctx->matchc = upa_readl(&ffb->matchc);		/* Buffer C Match Ops */
+	ctx->magnab = upa_readl(&ffb->magnab);		/* Buffer A/B Magnitude Ops */
+	ctx->magnc = upa_readl(&ffb->magnc);		/* Buffer C Magnitude Ops */
+	ctx->pmask = upa_readl(&ffb->pmask);		/* RGB Plane Mask */
+	ctx->xpmask = upa_readl(&ffb->xpmask);		/* X Plane Mask */
+	ctx->ypmask = upa_readl(&ffb->ypmask);		/* Y Plane Mask */
+	ctx->zpmask = upa_readl(&ffb->zpmask);		/* Z Plane Mask */
+
+	/* Auxiliary Clips. */
+	ctx->auxclip0min = upa_readl(&ffb->auxclip[0].min);
+	ctx->auxclip0max = upa_readl(&ffb->auxclip[0].max);
+	ctx->auxclip1min = upa_readl(&ffb->auxclip[1].min);
+	ctx->auxclip1max = upa_readl(&ffb->auxclip[1].max);
+	ctx->auxclip2min = upa_readl(&ffb->auxclip[2].min);
+	ctx->auxclip2max = upa_readl(&ffb->auxclip[2].max);
+	ctx->auxclip3min = upa_readl(&ffb->auxclip[3].min);
+	ctx->auxclip3max = upa_readl(&ffb->auxclip[3].max);
+
+	ctx->lpat = upa_readl(&ffb->lpat);		/* Line Pattern */
+	ctx->fontxy = upa_readl(&ffb->fontxy);		/* XY Font Coordinate */
+	ctx->fontw = upa_readl(&ffb->fontw);		/* Font Width */
+	ctx->fontinc = upa_readl(&ffb->fontinc);	/* Font X/Y Increment */
+
+	/* These registers/features only exist on FFB2 and later chips. */
+	if (fpriv->ffb_type >= ffb2_prototype) {
+		ctx->dcss1 = upa_readl(&ffb->dcss1);	/* Depth Cue Scale Slope 1 */
+		ctx->dcss2 = upa_readl(&ffb->dcss2);	/* Depth Cue Scale Slope 2 */
+		ctx->dcss2 = upa_readl(&ffb->dcss3);	/* Depth Cue Scale Slope 3 */
+		ctx->dcs2  = upa_readl(&ffb->dcs2);	/* Depth Cue Scale 2 */
+		ctx->dcs3  = upa_readl(&ffb->dcs3);	/* Depth Cue Scale 3 */
+		ctx->dcs4  = upa_readl(&ffb->dcs4);	/* Depth Cue Scale 4 */
+		ctx->dcd2  = upa_readl(&ffb->dcd2);	/* Depth Cue Depth 2 */
+		ctx->dcd3  = upa_readl(&ffb->dcd3);	/* Depth Cue Depth 3 */
+		ctx->dcd4  = upa_readl(&ffb->dcd4);	/* Depth Cue Depth 4 */
+
+		/* And stencil/stencilctl only exists on FFB2+ and later
+		 * due to the introduction of 3DRAM-III.
+		 */
+		if (fpriv->ffb_type == ffb2_vertical_plus ||
+		    fpriv->ffb_type == ffb2_horizontal_plus) {
+			ctx->stencil = upa_readl(&ffb->stencil);
+			ctx->stencilctl = upa_readl(&ffb->stencilctl);
+		}
+	}
+
+	/* Save the 32x32 area pattern. */
+	for (i = 0; i < 32; i++)
+		ctx->area_pattern[i] = upa_readl(&ffb->pattern[i]);
+
+	/* Finally, stash away the User Constol/Status Register. */
+	ctx->ucsr = upa_readl(&ffb->ucsr);
+}
+
+static void ffb_restore_context(ffb_dev_priv_t *fpriv, int old, int idx)
+{
+	ffb_fbcPtr ffb = fpriv->regs;
+	struct ffb_hw_context *ctx;
+	int i;
+
+	ctx = fpriv->hw_state[idx - 1];
+	if (idx == 0 || ctx == NULL)
+		return;
+
+	if (ctx->is_2d_only) {
+		/* 2D applications only care about certain pieces
+		 * of state.
+		 */
+		upa_writel(ctx->drawop, &ffb->drawop);
+
+		/* If we were restoring the vertex registers, this is where
+		 * we would do it.  We would restore 32 32-bit words starting
+		 * at ffb->suvtx.
+		 */
+
+		upa_writel(ctx->ppc, &ffb->ppc);
+		upa_writel(ctx->wid, &ffb->wid);
+		upa_writel(ctx->fg,  &ffb->fg);
+		upa_writel(ctx->bg, &ffb->bg);
+		upa_writel(ctx->xclip, &ffb->xclip);
+		upa_writel(ctx->fbc, &ffb->fbc);
+		upa_writel(ctx->rop, &ffb->rop);
+		upa_writel(ctx->cmp, &ffb->cmp);
+		upa_writel(ctx->matchab, &ffb->matchab);
+		upa_writel(ctx->magnab, &ffb->magnab);
+		upa_writel(ctx->pmask, &ffb->pmask);
+		upa_writel(ctx->xpmask, &ffb->xpmask);
+		upa_writel(ctx->lpat, &ffb->lpat);
+		upa_writel(ctx->fontxy, &ffb->fontxy);
+		upa_writel(ctx->fontw, &ffb->fontw);
+		upa_writel(ctx->fontinc, &ffb->fontinc);
+
+		/* stencil/stencilctl only exists on FFB2+ and later
+		 * due to the introduction of 3DRAM-III.
+		 */
+		if (fpriv->ffb_type == ffb2_vertical_plus ||
+		    fpriv->ffb_type == ffb2_horizontal_plus) {
+			upa_writel(ctx->stencil, &ffb->stencil);
+			upa_writel(ctx->stencilctl, &ffb->stencilctl);
+			upa_writel(0x80000000, &ffb->fbc);
+			upa_writel((ctx->stencilctl | 0x80000),
+				   &ffb->rawstencilctl);
+			upa_writel(ctx->fbc, &ffb->fbc);
+		}
+
+		for (i = 0; i < 32; i++)
+			upa_writel(ctx->area_pattern[i], &ffb->pattern[i]);
+		upa_writel((ctx->ucsr & 0xf0000), &ffb->ucsr);
+		return;
+	}
+
+	/* Restore drawop. */
+	upa_writel(ctx->drawop, &ffb->drawop);
+
+	/* If we were restoring the vertex registers, this is where
+	 * we would do it.  We would restore 32 32-bit words starting
+	 * at ffb->suvtx.
+	 */
+
+	/* Restore rendering attributes. */
+
+	upa_writel(ctx->ppc, &ffb->ppc);		/* Pixel Processor Control */
+	upa_writel(ctx->wid, &ffb->wid);		/* Current WID */
+	upa_writel(ctx->fg, &ffb->fg);			/* Constant FG color */
+	upa_writel(ctx->bg, &ffb->bg);			/* Constant BG color */
+	upa_writel(ctx->consty, &ffb->consty);		/* Constant Y */
+	upa_writel(ctx->constz, &ffb->constz);		/* Constant Z */
+	upa_writel(ctx->xclip, &ffb->xclip);		/* X plane clip */
+	upa_writel(ctx->dcss, &ffb->dcss);		/* Depth Cue Scale Slope */
+	upa_writel(ctx->vclipmin, &ffb->vclipmin);	/* Primary XY clip, minimum */
+	upa_writel(ctx->vclipmax, &ffb->vclipmax);	/* Primary XY clip, maximum */
+	upa_writel(ctx->vclipzmin, &ffb->vclipzmin);	/* Primary Z clip, minimum */
+	upa_writel(ctx->vclipzmax, &ffb->vclipzmax);	/* Primary Z clip, maximum */
+	upa_writel(ctx->dcsf, &ffb->dcsf);		/* Depth Cue Scale Front Bound */
+	upa_writel(ctx->dcsb, &ffb->dcsb);		/* Depth Cue Scale Back Bound */
+	upa_writel(ctx->dczf, &ffb->dczf);		/* Depth Cue Scale Z Front */
+	upa_writel(ctx->dczb, &ffb->dczb);		/* Depth Cue Scale Z Back */
+	upa_writel(ctx->blendc, &ffb->blendc);		/* Alpha Blend Control */
+	upa_writel(ctx->blendc1, &ffb->blendc1);	/* Alpha Blend Color 1 */
+	upa_writel(ctx->blendc2, &ffb->blendc2);	/* Alpha Blend Color 2 */
+	upa_writel(ctx->fbc, &ffb->fbc);		/* Frame Buffer Control */
+	upa_writel(ctx->rop, &ffb->rop);		/* Raster Operation */
+	upa_writel(ctx->cmp, &ffb->cmp);		/* Compare Controls */
+	upa_writel(ctx->matchab, &ffb->matchab);	/* Buffer A/B Match Ops */
+	upa_writel(ctx->matchc, &ffb->matchc);		/* Buffer C Match Ops */
+	upa_writel(ctx->magnab, &ffb->magnab);		/* Buffer A/B Magnitude Ops */
+	upa_writel(ctx->magnc, &ffb->magnc);		/* Buffer C Magnitude Ops */
+	upa_writel(ctx->pmask, &ffb->pmask);		/* RGB Plane Mask */
+	upa_writel(ctx->xpmask, &ffb->xpmask);		/* X Plane Mask */
+	upa_writel(ctx->ypmask, &ffb->ypmask);		/* Y Plane Mask */
+	upa_writel(ctx->zpmask, &ffb->zpmask);		/* Z Plane Mask */
+
+	/* Auxiliary Clips. */
+	upa_writel(ctx->auxclip0min, &ffb->auxclip[0].min);
+	upa_writel(ctx->auxclip0max, &ffb->auxclip[0].max);
+	upa_writel(ctx->auxclip1min, &ffb->auxclip[1].min);
+	upa_writel(ctx->auxclip1max, &ffb->auxclip[1].max);
+	upa_writel(ctx->auxclip2min, &ffb->auxclip[2].min);
+	upa_writel(ctx->auxclip2max, &ffb->auxclip[2].max);
+	upa_writel(ctx->auxclip3min, &ffb->auxclip[3].min);
+	upa_writel(ctx->auxclip3max, &ffb->auxclip[3].max);
+
+	upa_writel(ctx->lpat, &ffb->lpat);		/* Line Pattern */
+	upa_writel(ctx->fontxy, &ffb->fontxy);		/* XY Font Coordinate */
+	upa_writel(ctx->fontw, &ffb->fontw);		/* Font Width */
+	upa_writel(ctx->fontinc, &ffb->fontinc);	/* Font X/Y Increment */
+
+	/* These registers/features only exist on FFB2 and later chips. */
+	if (fpriv->ffb_type >= ffb2_prototype) {
+		upa_writel(ctx->dcss1, &ffb->dcss1);	/* Depth Cue Scale Slope 1 */
+		upa_writel(ctx->dcss2, &ffb->dcss2);	/* Depth Cue Scale Slope 2 */
+		upa_writel(ctx->dcss3, &ffb->dcss2);	/* Depth Cue Scale Slope 3 */
+		upa_writel(ctx->dcs2, &ffb->dcs2);	/* Depth Cue Scale 2 */
+		upa_writel(ctx->dcs3, &ffb->dcs3);	/* Depth Cue Scale 3 */
+		upa_writel(ctx->dcs4, &ffb->dcs4);	/* Depth Cue Scale 4 */
+		upa_writel(ctx->dcd2, &ffb->dcd2);	/* Depth Cue Depth 2 */
+		upa_writel(ctx->dcd3, &ffb->dcd3);	/* Depth Cue Depth 3 */
+		upa_writel(ctx->dcd4, &ffb->dcd4);	/* Depth Cue Depth 4 */
+
+		/* And stencil/stencilctl only exists on FFB2+ and later
+		 * due to the introduction of 3DRAM-III.
+		 */
+		if (fpriv->ffb_type == ffb2_vertical_plus ||
+		    fpriv->ffb_type == ffb2_horizontal_plus) {
+			/* Unfortunately, there is a hardware bug on
+			 * the FFB2+ chips which prevents a normal write
+			 * to the stencil control register from working
+			 * as it should.
+			 *
+			 * The state controlled by the FFB stencilctl register
+			 * really gets transferred to the per-buffer instances
+			 * of the stencilctl register in the 3DRAM chips.
+			 *
+			 * The bug is that FFB does not update buffer C correctly,
+			 * so we have to do it by hand for them.
+			 */
+
+			/* This will update buffers A and B. */
+			upa_writel(ctx->stencil, &ffb->stencil);
+			upa_writel(ctx->stencilctl, &ffb->stencilctl);
+
+			/* Force FFB to use buffer C 3dram regs. */
+			upa_writel(0x80000000, &ffb->fbc);
+			upa_writel((ctx->stencilctl | 0x80000),
+				   &ffb->rawstencilctl);
+
+			/* Now restore the correct FBC controls. */
+			upa_writel(ctx->fbc, &ffb->fbc);
+		}
+	}
+
+	/* Restore the 32x32 area pattern. */
+	for (i = 0; i < 32; i++)
+		upa_writel(ctx->area_pattern[i], &ffb->pattern[i]);
+
+	/* Finally, stash away the User Constol/Status Register.
+	 * The only state we really preserve here is the picking
+	 * control.
+	 */
+	upa_writel((ctx->ucsr & 0xf0000), &ffb->ucsr);
+}
+
+#define FFB_UCSR_FB_BUSY       0x01000000
+#define FFB_UCSR_RP_BUSY       0x02000000
+#define FFB_UCSR_ALL_BUSY      (FFB_UCSR_RP_BUSY|FFB_UCSR_FB_BUSY)
+
+static void FFBWait(ffb_fbcPtr ffb)
+{
+	int limit = 100000;
+
+	do {
+		u32 regval = upa_readl(&ffb->ucsr);
+
+		if ((regval & FFB_UCSR_ALL_BUSY) == 0)
+			break;
+	} while (--limit);
+}
+
+int ffb_context_switch(drm_device_t *dev, int old, int new)
+{
+	ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) (dev + 1);
+
+        atomic_inc(&dev->total_ctx);
+
+#if DRM_DMA_HISTOGRAM
+        dev->ctx_start = get_cycles();
+#endif
+        
+        DRM_DEBUG("Context switch from %d to %d\n", old, new);
+
+        if (new == dev->last_context ||
+	    dev->last_context == 0) {
+		dev->last_context = new;
+                return 0;
+	}
+        
+	FFBWait(fpriv->regs);
+	ffb_save_context(fpriv, old);
+	ffb_restore_context(fpriv, old, new);
+	FFBWait(fpriv->regs);
+        
+	dev->last_context = new;
+
+        return 0;
+}
+
+int ffb_resctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_ctx_res_t	res;
+	drm_ctx_t	ctx;
+	int		i;
+
+	DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS);
+	if (copy_from_user(&res, (drm_ctx_res_t *)arg, sizeof(res)))
+		return -EFAULT;
+	if (res.count >= DRM_RESERVED_CONTEXTS) {
+		memset(&ctx, 0, sizeof(ctx));
+		for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
+			ctx.handle = i;
+			if (copy_to_user(&res.contexts[i],
+					 &i,
+					 sizeof(i)))
+				return -EFAULT;
+		}
+	}
+	res.count = DRM_RESERVED_CONTEXTS;
+	if (copy_to_user((drm_ctx_res_t *)arg, &res, sizeof(res)))
+		return -EFAULT;
+	return 0;
+}
+
+
+int ffb_addctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+	int idx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	idx = ffb_alloc_queue(dev, (ctx.flags & _DRM_CONTEXT_2DONLY));
+	if (idx < 0)
+		return -ENFILE;
+
+	DRM_DEBUG("%d\n", ctx.handle);
+	ctx.handle = idx;
+	if (copy_to_user((drm_ctx_t *)arg, &ctx, sizeof(ctx)))
+		return -EFAULT;
+	return 0;
+}
+
+int ffb_modctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	ffb_dev_priv_t	*fpriv	= (ffb_dev_priv_t *) (dev + 1);
+	struct ffb_hw_context *hwctx;
+	drm_ctx_t ctx;
+	int idx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx)))
+		return -EFAULT;
+
+	idx = ctx.handle;
+	if (idx <= 0 || idx >= FFB_MAX_CTXS)
+		return -EINVAL;
+
+	hwctx = fpriv->hw_state[idx - 1];
+	if (hwctx == NULL)
+		return -EINVAL;
+
+	if ((ctx.flags & _DRM_CONTEXT_2DONLY) == 0)
+		hwctx->is_2d_only = 0;
+	else
+		hwctx->is_2d_only = 1;
+
+	return 0;
+}
+
+int ffb_getctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	ffb_dev_priv_t	*fpriv	= (ffb_dev_priv_t *) (dev + 1);
+	struct ffb_hw_context *hwctx;
+	drm_ctx_t ctx;
+	int idx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx)))
+		return -EFAULT;
+
+	idx = ctx.handle;
+	if (idx <= 0 || idx >= FFB_MAX_CTXS)
+		return -EINVAL;
+
+	hwctx = fpriv->hw_state[idx - 1];
+	if (hwctx == NULL)
+		return -EINVAL;
+
+	if (hwctx->is_2d_only != 0)
+		ctx.flags = _DRM_CONTEXT_2DONLY;
+	else
+		ctx.flags = 0;
+
+	if (copy_to_user((drm_ctx_t*)arg, &ctx, sizeof(ctx)))
+		return -EFAULT;
+
+	return 0;
+}
+
+int ffb_switchctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	return ffb_context_switch(dev, dev->last_context, ctx.handle);
+}
+
+int ffb_newctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+
+	return 0;
+}
+
+int ffb_rmctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_ctx_t	ctx;
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	ffb_dev_priv_t	*fpriv	= (ffb_dev_priv_t *) (dev + 1);
+	int idx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+
+	idx = ctx.handle - 1;
+	if (idx < 0 || idx >= FFB_MAX_CTXS)
+		return -EINVAL;
+
+	if (fpriv->hw_state[idx] != NULL) {
+		kfree(fpriv->hw_state[idx]);
+		fpriv->hw_state[idx] = NULL;
+	}
+	return 0;
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/ffb_drv.c linux/drivers/char/drm-4.0/ffb_drv.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/ffb_drv.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/ffb_drv.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,952 @@
+/* $Id: ffb_drv.c,v 1.1 2002/01/09 21:56:59 marcelo Exp $
+ * ffb_drv.c: Creator/Creator3D direct rendering driver.
+ *
+ * Copyright (C) 2000 David S. Miller (davem@redhat.com)
+ */
+
+#include "drmP.h"
+
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <asm/shmparam.h>
+#include <asm/oplib.h>
+#include <asm/upa.h>
+
+#include "ffb_drv.h"
+
+#define FFB_NAME	"ffb"
+#define FFB_DESC	"Creator/Creator3D"
+#define FFB_DATE	"20000517"
+#define FFB_MAJOR	0
+#define FFB_MINOR	0
+#define FFB_PATCHLEVEL	1
+
+/* Forward declarations. */
+int  ffb_init(void);
+void ffb_cleanup(void);
+static int  ffb_version(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+static int  ffb_open(struct inode *inode, struct file *filp);
+static int  ffb_release(struct inode *inode, struct file *filp);
+static int  ffb_ioctl(struct inode *inode, struct file *filp,
+		      unsigned int cmd, unsigned long arg);
+static int  ffb_lock(struct inode *inode, struct file *filp,
+		     unsigned int cmd, unsigned long arg);
+static int  ffb_unlock(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+static int ffb_mmap(struct file *filp, struct vm_area_struct *vma);
+static unsigned long ffb_get_unmapped_area(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
+
+/* From ffb_context.c */
+extern int ffb_resctx(struct inode *, struct file *, unsigned int, unsigned long);
+extern int ffb_addctx(struct inode *, struct file *, unsigned int, unsigned long);
+extern int ffb_modctx(struct inode *, struct file *, unsigned int, unsigned long);
+extern int ffb_getctx(struct inode *, struct file *, unsigned int, unsigned long);
+extern int ffb_switchctx(struct inode *, struct file *, unsigned int, unsigned long);
+extern int ffb_newctx(struct inode *, struct file *, unsigned int, unsigned long);
+extern int ffb_rmctx(struct inode *, struct file *, unsigned int, unsigned long);
+extern int ffb_context_switch(drm_device_t *, int, int);
+
+static struct file_operations ffb_fops = {
+	owner:			THIS_MODULE,
+	open:			ffb_open,
+	flush:			drm_flush,
+	release:		ffb_release,
+	ioctl:			ffb_ioctl,
+	mmap:			ffb_mmap,
+	read:			drm_read,
+	fasync:			drm_fasync,
+	poll:			drm_poll,
+	get_unmapped_area:	ffb_get_unmapped_area,
+};
+
+/* This is just a template, we make a new copy for each FFB
+ * we discover at init time so that each one gets a unique
+ * misc device minor number.
+ */
+static struct miscdevice ffb_misc = {
+	minor:	MISC_DYNAMIC_MINOR,
+	name:	FFB_NAME,
+	fops:	&ffb_fops,
+};
+
+static drm_ioctl_desc_t ffb_ioctls[] = {
+	[DRM_IOCTL_NR(DRM_IOCTL_VERSION)]    = { ffb_version,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)] = { drm_getunique,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)]  = { drm_getmagic,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)]  = { drm_irq_busid,	  0, 1 }, /* XXX */
+
+	[DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)] = { drm_setunique,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_BLOCK)]	     = { drm_block,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)]    = { drm_unblock,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)] = { drm_authmagic,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)]    = { drm_addmap,	  1, 1 },
+	
+	/* The implementation is currently a nop just like on tdfx.
+	 * Later we can do something more clever. -DaveM
+	 */
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)]    = { ffb_addctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)]     = { ffb_rmctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)]    = { ffb_modctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)]    = { ffb_getctx,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { ffb_switchctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)]    = { ffb_newctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)]    = { ffb_resctx,	  1, 0 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)]   = { drm_adddraw,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)]    = { drm_rmdraw,	  1, 1 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_LOCK)]	     = { ffb_lock,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)]     = { ffb_unlock,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FINISH)]     = { drm_finish,	  1, 0 },
+};
+#define FFB_IOCTL_COUNT DRM_ARRAY_SIZE(ffb_ioctls)
+
+#ifdef MODULE
+static char *ffb = NULL;
+#endif
+
+MODULE_AUTHOR("David S. Miller (davem@redhat.com)");
+MODULE_DESCRIPTION("Sun Creator/Creator3D DRI");
+MODULE_LICENSE("GPL");
+
+static int ffb_takedown(drm_device_t *dev)
+{
+	int		  i;
+	drm_magic_entry_t *pt, *next;
+	drm_map_t	  *map;
+	drm_vma_entry_t	  *vma, *vma_next;
+
+	DRM_DEBUG("\n");
+
+	down(&dev->struct_sem);
+	del_timer(&dev->timer);
+	
+	if (dev->devname) {
+		drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER);
+		dev->devname = NULL;
+	}
+	
+	if (dev->unique) {
+		drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER);
+		dev->unique = NULL;
+		dev->unique_len = 0;
+	}
+
+	/* Clear pid list */
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		for (pt = dev->magiclist[i].head; pt; pt = next) {
+			next = pt->next;
+			drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
+		}
+		dev->magiclist[i].head = dev->magiclist[i].tail = NULL;
+	}
+	
+	/* Clear vma list (only built for debugging) */
+	if (dev->vmalist) {
+		for (vma = dev->vmalist; vma; vma = vma_next) {
+			vma_next = vma->next;
+			drm_free(vma, sizeof(*vma), DRM_MEM_VMAS);
+		}
+		dev->vmalist = NULL;
+	}
+	
+	/* Clear map area information */
+	if (dev->maplist) {
+		for (i = 0; i < dev->map_count; i++) {
+			map = dev->maplist[i];
+			switch (map->type) {
+			case _DRM_REGISTERS:
+			case _DRM_FRAME_BUFFER:
+				drm_ioremapfree(map->handle, map->size);
+				break;
+
+			case _DRM_SHM:
+				drm_free_pages((unsigned long)map->handle,
+					       drm_order(map->size)
+					       - PAGE_SHIFT,
+					       DRM_MEM_SAREA);
+				break;
+
+			default:
+				break;
+			};
+
+			drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+		}
+
+		drm_free(dev->maplist,
+			 dev->map_count * sizeof(*dev->maplist),
+			 DRM_MEM_MAPS);
+		dev->maplist   = NULL;
+		dev->map_count = 0;
+	}
+	
+	if (dev->lock.hw_lock) {
+		dev->lock.hw_lock    = NULL; /* SHM removed */
+		dev->lock.pid	     = 0;
+		wake_up_interruptible(&dev->lock.lock_queue);
+	}
+	up(&dev->struct_sem);
+	
+	return 0;
+}
+
+drm_device_t **ffb_dev_table;
+static int ffb_dev_table_size;
+
+static void get_ffb_type(ffb_dev_priv_t *ffb_priv, int instance)
+{
+	volatile unsigned char *strap_bits;
+	unsigned char val;
+
+	strap_bits = (volatile unsigned char *)
+		(ffb_priv->card_phys_base + 0x00200000UL);
+
+	/* Don't ask, you have to read the value twice for whatever
+	 * reason to get correct contents.
+	 */
+	val = upa_readb(strap_bits);
+	val = upa_readb(strap_bits);
+	switch (val & 0x78) {
+	case (0x0 << 5) | (0x0 << 3):
+		ffb_priv->ffb_type = ffb1_prototype;
+		printk("ffb%d: Detected FFB1 pre-FCS prototype\n", instance);
+		break;
+	case (0x0 << 5) | (0x1 << 3):
+		ffb_priv->ffb_type = ffb1_standard;
+		printk("ffb%d: Detected FFB1\n", instance);
+		break;
+	case (0x0 << 5) | (0x3 << 3):
+		ffb_priv->ffb_type = ffb1_speedsort;
+		printk("ffb%d: Detected FFB1-SpeedSort\n", instance);
+		break;
+	case (0x1 << 5) | (0x0 << 3):
+		ffb_priv->ffb_type = ffb2_prototype;
+		printk("ffb%d: Detected FFB2/vertical pre-FCS prototype\n", instance);
+		break;
+	case (0x1 << 5) | (0x1 << 3):
+		ffb_priv->ffb_type = ffb2_vertical;
+		printk("ffb%d: Detected FFB2/vertical\n", instance);
+		break;
+	case (0x1 << 5) | (0x2 << 3):
+		ffb_priv->ffb_type = ffb2_vertical_plus;
+		printk("ffb%d: Detected FFB2+/vertical\n", instance);
+		break;
+	case (0x2 << 5) | (0x0 << 3):
+		ffb_priv->ffb_type = ffb2_horizontal;
+		printk("ffb%d: Detected FFB2/horizontal\n", instance);
+		break;
+	case (0x2 << 5) | (0x2 << 3):
+		ffb_priv->ffb_type = ffb2_horizontal;
+		printk("ffb%d: Detected FFB2+/horizontal\n", instance);
+		break;
+	default:
+		ffb_priv->ffb_type = ffb2_vertical;
+		printk("ffb%d: Unknown boardID[%08x], assuming FFB2\n", instance, val);
+		break;
+	};
+}
+
+static void __init ffb_apply_upa_parent_ranges(int parent, struct linux_prom64_registers *regs)
+{
+	struct linux_prom64_ranges ranges[PROMREG_MAX];
+	char name[128];
+	int len, i;
+
+	prom_getproperty(parent, "name", name, sizeof(name));
+	if (strcmp(name, "upa") != 0)
+		return;
+
+	len = prom_getproperty(parent, "ranges", (void *) ranges, sizeof(ranges));
+	if (len <= 0)
+		return;
+
+	len /= sizeof(struct linux_prom64_ranges);
+	for (i = 0; i < len; i++) {
+		struct linux_prom64_ranges *rng = &ranges[i];
+		u64 phys_addr = regs->phys_addr;
+
+		if (phys_addr >= rng->ot_child_base &&
+		    phys_addr < (rng->ot_child_base + rng->or_size)) {
+			regs->phys_addr -= rng->ot_child_base;
+			regs->phys_addr += rng->ot_parent_base;
+			return;
+		}
+	}
+
+	return;
+}
+
+static int __init ffb_init_one(int prom_node, int parent_node, int instance)
+{
+	struct linux_prom64_registers regs[2*PROMREG_MAX];
+	drm_device_t *dev;
+	ffb_dev_priv_t *ffb_priv;
+	int ret, i;
+
+	dev = kmalloc(sizeof(drm_device_t) + sizeof(ffb_dev_priv_t), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	memset(dev, 0, sizeof(*dev));
+	spin_lock_init(&dev->count_lock);
+	sema_init(&dev->struct_sem, 1);
+
+	ffb_priv = (ffb_dev_priv_t *) (dev + 1);
+	ffb_priv->prom_node = prom_node;
+	if (prom_getproperty(ffb_priv->prom_node, "reg",
+			     (void *)regs, sizeof(regs)) <= 0) {
+		kfree(dev);
+		return -EINVAL;
+	}
+	ffb_apply_upa_parent_ranges(parent_node, &regs[0]);
+	ffb_priv->card_phys_base = regs[0].phys_addr;
+	ffb_priv->regs = (ffb_fbcPtr)
+		(regs[0].phys_addr + 0x00600000UL);
+	get_ffb_type(ffb_priv, instance);
+	for (i = 0; i < FFB_MAX_CTXS; i++)
+		ffb_priv->hw_state[i] = NULL;
+
+	ffb_dev_table[instance] = dev;
+
+#ifdef MODULE
+	drm_parse_options(ffb);
+#endif
+
+	memcpy(&ffb_priv->miscdev, &ffb_misc, sizeof(ffb_misc));
+	ret = misc_register(&ffb_priv->miscdev);
+	if (ret) {
+		ffb_dev_table[instance] = NULL;
+		kfree(dev);
+		return ret;
+	}
+
+	dev->device = MKDEV(MISC_MAJOR, ffb_priv->miscdev.minor);
+	dev->name = FFB_NAME;
+
+	drm_mem_init();
+	drm_proc_init(dev);
+
+	DRM_INFO("Initialized %s %d.%d.%d %s on minor %d at %016lx\n",
+		 FFB_NAME,
+		 FFB_MAJOR,
+		 FFB_MINOR,
+		 FFB_PATCHLEVEL,
+		 FFB_DATE,
+		 ffb_priv->miscdev.minor,
+		 ffb_priv->card_phys_base);
+	
+	return 0;
+}
+
+static int __init ffb_count_siblings(int root)
+{
+	int node, child, count = 0;
+
+	child = prom_getchild(root);
+	for (node = prom_searchsiblings(child, "SUNW,ffb"); node;
+	     node = prom_searchsiblings(prom_getsibling(node), "SUNW,ffb"))
+		count++;
+
+	return count;
+}
+
+static int __init ffb_init_dev_table(void)
+{
+	int root, total;
+
+	total = ffb_count_siblings(prom_root_node);
+	root = prom_getchild(prom_root_node);
+	for (root = prom_searchsiblings(root, "upa"); root;
+	     root = prom_searchsiblings(prom_getsibling(root), "upa"))
+		total += ffb_count_siblings(root);
+
+	if (!total)
+		return -ENODEV;
+
+	ffb_dev_table = kmalloc(sizeof(drm_device_t *) * total, GFP_KERNEL);
+	if (!ffb_dev_table)
+		return -ENOMEM;
+
+	ffb_dev_table_size = total;
+
+	return 0;
+}
+
+static int __init ffb_scan_siblings(int root, int instance)
+{
+	int node, child;
+
+	child = prom_getchild(root);
+	for (node = prom_searchsiblings(child, "SUNW,ffb"); node;
+	     node = prom_searchsiblings(prom_getsibling(node), "SUNW,ffb")) {
+		ffb_init_one(node, root, instance);
+		instance++;
+	}
+
+	return instance;
+}
+
+int __init ffb_init(void)
+{
+	int root, instance, ret;
+
+	ret = ffb_init_dev_table();
+	if (ret)
+		return ret;
+
+	instance = ffb_scan_siblings(prom_root_node, 0);
+
+	root = prom_getchild(prom_root_node);
+	for (root = prom_searchsiblings(root, "upa"); root;
+	     root = prom_searchsiblings(prom_getsibling(root), "upa"))
+		instance = ffb_scan_siblings(root, instance);
+
+	return 0;
+}
+
+void __exit ffb_cleanup(void)
+{
+	int instance;
+
+	DRM_DEBUG("\n");
+	
+	drm_proc_cleanup();
+	for (instance = 0; instance < ffb_dev_table_size; instance++) {
+		drm_device_t *dev = ffb_dev_table[instance];
+		ffb_dev_priv_t *ffb_priv;
+
+		if (!dev)
+			continue;
+
+		ffb_priv = (ffb_dev_priv_t *) (dev + 1);
+		if (misc_deregister(&ffb_priv->miscdev)) {
+			DRM_ERROR("Cannot unload module\n");
+		} else {
+			DRM_INFO("Module unloaded\n");
+		}
+		ffb_takedown(dev);
+		kfree(dev);
+		ffb_dev_table[instance] = NULL;
+	}
+	kfree(ffb_dev_table);
+	ffb_dev_table = NULL;
+	ffb_dev_table_size = 0;
+}
+
+static int ffb_version(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	drm_version_t version;
+	int len, ret;
+
+	ret = copy_from_user(&version, (drm_version_t *)arg, sizeof(version));
+	if (ret)
+		return -EFAULT;
+
+	version.version_major		= FFB_MAJOR;
+	version.version_minor		= FFB_MINOR;
+	version.version_patchlevel	= FFB_PATCHLEVEL;
+
+	len = strlen(FFB_NAME);
+	if (len > version.name_len)
+		len = version.name_len;
+	version.name_len = len;
+	if (len && version.name) {
+		ret = copy_to_user(version.name, FFB_NAME, len);
+		if (ret)
+			return -EFAULT;
+	}
+
+	len = strlen(FFB_DATE);
+	if (len > version.date_len)
+		len = version.date_len;
+	version.date_len = len;
+	if (len && version.date) {
+		ret = copy_to_user(version.date, FFB_DATE, len);
+		if (ret)
+			return -EFAULT;
+	}
+
+	len = strlen(FFB_DESC);
+	if (len > version.desc_len)
+		len = version.desc_len;
+	version.desc_len = len;
+	if (len && version.desc) {
+		ret = copy_to_user(version.desc, FFB_DESC, len);
+		if (ret)
+			return -EFAULT;
+	}
+
+	ret = copy_to_user((drm_version_t *) arg, &version, sizeof(version));
+	if (ret)
+		ret = -EFAULT;
+
+	return ret;
+}
+
+static int ffb_setup(drm_device_t *dev)
+{
+	int i;
+
+	atomic_set(&dev->ioctl_count, 0);
+	atomic_set(&dev->vma_count, 0);
+	dev->buf_use = 0;
+	atomic_set(&dev->buf_alloc, 0);
+
+	atomic_set(&dev->total_open, 0);
+	atomic_set(&dev->total_close, 0);
+	atomic_set(&dev->total_ioctl, 0);
+	atomic_set(&dev->total_irq, 0);
+	atomic_set(&dev->total_ctx, 0);
+	atomic_set(&dev->total_locks, 0);
+	atomic_set(&dev->total_unlocks, 0);
+	atomic_set(&dev->total_contends, 0);
+	atomic_set(&dev->total_sleeps, 0);
+
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		dev->magiclist[i].head = NULL;
+		dev->magiclist[i].tail = NULL;
+	}
+
+	dev->maplist	    = NULL;
+	dev->map_count	    = 0;
+	dev->vmalist	    = NULL;
+	dev->lock.hw_lock   = NULL;
+	init_waitqueue_head(&dev->lock.lock_queue);
+	dev->queue_count    = 0;
+	dev->queue_reserved = 0;
+	dev->queue_slots    = 0;
+	dev->queuelist	    = NULL;
+	dev->irq	    = 0;
+	dev->context_flag   = 0;
+	dev->interrupt_flag = 0;
+	dev->dma            = 0;
+	dev->dma_flag	    = 0;
+	dev->last_context   = 0;
+	dev->last_switch    = 0;
+	dev->last_checked   = 0;
+	init_timer(&dev->timer);
+	init_waitqueue_head(&dev->context_wait);
+
+	dev->ctx_start	    = 0;
+	dev->lck_start	    = 0;
+	
+	dev->buf_rp	  = dev->buf;
+	dev->buf_wp	  = dev->buf;
+	dev->buf_end	  = dev->buf + DRM_BSZ;
+	dev->buf_async	  = NULL;
+	init_waitqueue_head(&dev->buf_readers);
+	init_waitqueue_head(&dev->buf_writers);
+
+	return 0;
+}
+
+static int ffb_open(struct inode *inode, struct file *filp)
+{
+	drm_device_t *dev;
+	int minor, i;
+	int ret = 0;
+
+	minor = MINOR(inode->i_rdev);
+	for (i = 0; i < ffb_dev_table_size; i++) {
+		ffb_dev_priv_t *ffb_priv;
+
+		ffb_priv = (ffb_dev_priv_t *) (ffb_dev_table[i] + 1);
+
+		if (ffb_priv->miscdev.minor == minor)
+			break;
+	}
+
+	if (i >= ffb_dev_table_size)
+		return -EINVAL;
+
+	dev = ffb_dev_table[i];
+	if (!dev)
+		return -EINVAL;
+
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	ret = drm_open_helper(inode, filp, dev);
+	if (!ret) {
+		atomic_inc(&dev->total_open);
+		spin_lock(&dev->count_lock);
+		if (!dev->open_count++) {
+			spin_unlock(&dev->count_lock);
+			return ffb_setup(dev);
+		}
+		spin_unlock(&dev->count_lock);
+	}
+
+	return ret;
+}
+
+static int ffb_release(struct inode *inode, struct file *filp)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev;
+	int ret = 0;
+
+	lock_kernel();
+	dev = priv->dev;
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	if (dev->lock.hw_lock != NULL
+	    && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)
+	    && dev->lock.pid == current->pid) {
+		ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) (dev + 1);
+		int context = _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock);
+		int idx;
+
+		/* We have to free up the rogue hw context state
+		 * holding error or else we will leak it.
+		 */
+		idx = context - 1;
+		if (fpriv->hw_state[idx] != NULL) {
+			kfree(fpriv->hw_state[idx]);
+			fpriv->hw_state[idx] = NULL;
+		}
+	}
+
+	ret = drm_release(inode, filp);
+
+	if (!ret) {
+		atomic_inc(&dev->total_close);
+		spin_lock(&dev->count_lock);
+		if (!--dev->open_count) {
+			if (atomic_read(&dev->ioctl_count) || dev->blocked) {
+				DRM_ERROR("Device busy: %d %d\n",
+					  atomic_read(&dev->ioctl_count),
+					  dev->blocked);
+				spin_unlock(&dev->count_lock);
+				unlock_kernel();
+				return -EBUSY;
+			}
+			spin_unlock(&dev->count_lock);
+			ret = ffb_takedown(dev);
+			unlock_kernel();
+			return ret;
+		}
+		spin_unlock(&dev->count_lock);
+	}
+
+	unlock_kernel();
+	return ret;
+}
+
+static int ffb_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	int		 nr	 = DRM_IOCTL_NR(cmd);
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_ioctl_desc_t *ioctl;
+	drm_ioctl_t	 *func;
+	int		 ret;
+
+	atomic_inc(&dev->ioctl_count);
+	atomic_inc(&dev->total_ioctl);
+	++priv->ioctl_count;
+	
+	DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n",
+		  current->pid, cmd, nr, dev->device, priv->authenticated);
+
+	if (nr >= FFB_IOCTL_COUNT) {
+		ret = -EINVAL;
+	} else {
+		ioctl	  = &ffb_ioctls[nr];
+		func	  = ioctl->func;
+
+		if (!func) {
+			DRM_DEBUG("no function\n");
+			ret = -EINVAL;
+		} else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN))
+			    || (ioctl->auth_needed && !priv->authenticated)) {
+			ret = -EACCES;
+		} else {
+			ret = (func)(inode, filp, cmd, arg);
+		}
+	}
+	
+	atomic_dec(&dev->ioctl_count);
+
+	return ret;
+}
+
+static int ffb_lock(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+        drm_file_t        *priv	= filp->private_data;
+        drm_device_t      *dev	= priv->dev;
+        DECLARE_WAITQUEUE(entry, current);
+        int               ret	= 0;
+        drm_lock_t        lock;
+
+	ret = copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock));
+	if (ret)
+		return -EFAULT;
+
+        if (lock.context == DRM_KERNEL_CONTEXT) {
+                DRM_ERROR("Process %d using kernel context %d\n",
+                          current->pid, lock.context);
+                return -EINVAL;
+        }
+
+        DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
+                  lock.context, current->pid, dev->lock.hw_lock->lock,
+                  lock.flags);
+
+	add_wait_queue(&dev->lock.lock_queue, &entry);
+	for (;;) {
+		if (!dev->lock.hw_lock) {
+			/* Device has been unregistered */
+			ret = -EINTR;
+			break;
+		}
+		if (drm_lock_take(&dev->lock.hw_lock->lock,
+				  lock.context)) {
+			dev->lock.pid       = current->pid;
+			dev->lock.lock_time = jiffies;
+			atomic_inc(&dev->total_locks);
+			break;  /* Got lock */
+		}
+                        
+		/* Contention */
+		atomic_inc(&dev->total_sleeps);
+		current->state = TASK_INTERRUPTIBLE;
+		current->policy |= SCHED_YIELD;
+		schedule();
+		if (signal_pending(current)) {
+			ret = -ERESTARTSYS;
+			break;
+		}
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&dev->lock.lock_queue, &entry);
+
+        if (!ret) {
+		sigemptyset(&dev->sigmask);
+		sigaddset(&dev->sigmask, SIGSTOP);
+		sigaddset(&dev->sigmask, SIGTSTP);
+		sigaddset(&dev->sigmask, SIGTTIN);
+		sigaddset(&dev->sigmask, SIGTTOU);
+		dev->sigdata.context = lock.context;
+		dev->sigdata.lock = dev->lock.hw_lock;
+		block_all_signals(drm_notifier, &dev->sigdata, &dev->sigmask);
+
+		if (dev->last_context != lock.context)
+			ffb_context_switch(dev, dev->last_context, lock.context);
+	}
+
+        DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
+
+        return ret;
+}
+
+int ffb_unlock(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	drm_lock_t	  lock;
+	unsigned int old, new, prev, ctx;
+	int ret;
+
+	ret = copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock));
+	if (ret)
+		return -EFAULT;
+	
+	if ((ctx = lock.context) == DRM_KERNEL_CONTEXT) {
+		DRM_ERROR("Process %d using kernel context %d\n",
+			  current->pid, lock.context);
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("%d frees lock (%d holds)\n",
+		  lock.context,
+		  _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+	atomic_inc(&dev->total_unlocks);
+	if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock))
+		atomic_inc(&dev->total_contends);
+
+	/* We no longer really hold it, but if we are the next
+	 * agent to request it then we should just be able to
+	 * take it immediately and not eat the ioctl.
+	 */
+	dev->lock.pid = 0;
+	{
+		__volatile__ unsigned int *plock = &dev->lock.hw_lock->lock;
+
+		do {
+			old  = *plock;
+			new  = ctx;
+			prev = cmpxchg(plock, old, new);
+		} while (prev != old);
+	}
+
+	wake_up_interruptible(&dev->lock.lock_queue);
+	
+	unblock_all_signals();
+	return 0;
+}
+
+extern struct vm_operations_struct drm_vm_ops;
+extern struct vm_operations_struct drm_vm_shm_ops;
+extern struct vm_operations_struct drm_vm_shm_lock_ops;
+
+static int ffb_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_map_t	*map	= NULL;
+	ffb_dev_priv_t	*ffb_priv;
+	int		i, minor;
+	
+	DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n",
+		  vma->vm_start, vma->vm_end, VM_OFFSET(vma));
+
+	minor = MINOR(filp->f_dentry->d_inode->i_rdev);
+	ffb_priv = NULL;
+	for (i = 0; i < ffb_dev_table_size; i++) {
+		ffb_priv = (ffb_dev_priv_t *) (ffb_dev_table[i] + 1);
+		if (ffb_priv->miscdev.minor == minor)
+			break;
+	}
+	if (i >= ffb_dev_table_size)
+		return -EINVAL;
+
+	/* We don't support/need dma mappings, so... */
+	if (!VM_OFFSET(vma))
+		return -EINVAL;
+
+	for (i = 0; i < dev->map_count; i++) {
+		unsigned long off;
+
+		map = dev->maplist[i];
+
+		/* Ok, a little hack to make 32-bit apps work. */
+		off = (map->offset & 0xffffffff);
+		if (off == VM_OFFSET(vma))
+			break;
+	}
+
+	if (i >= dev->map_count)
+		return -EINVAL;
+
+	if (!map ||
+	    ((map->flags & _DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN)))
+		return -EPERM;
+
+	if (map->size != (vma->vm_end - vma->vm_start))
+		return -EINVAL;
+
+	/* Set read-only attribute before mappings are created
+	 * so it works for fb/reg maps too.
+	 */
+	if (map->flags & _DRM_READ_ONLY)
+		vma->vm_page_prot = __pgprot(pte_val(pte_wrprotect(
+			__pte(pgprot_val(vma->vm_page_prot)))));
+
+	switch (map->type) {
+	case _DRM_FRAME_BUFFER:
+		/* FALLTHROUGH */
+
+	case _DRM_REGISTERS:
+		/* In order to handle 32-bit drm apps/xserver we
+		 * play a trick.  The mappings only really specify
+		 * the 32-bit offset from the cards 64-bit base
+		 * address, and we just add in the base here.
+		 */
+		vma->vm_flags |= VM_IO;
+		if (io_remap_page_range(vma->vm_start,
+					ffb_priv->card_phys_base + VM_OFFSET(vma),
+					vma->vm_end - vma->vm_start,
+					vma->vm_page_prot, 0))
+			return -EAGAIN;
+
+		vma->vm_ops = &drm_vm_ops;
+		break;
+	case _DRM_SHM:
+		if (map->flags & _DRM_CONTAINS_LOCK)
+			vma->vm_ops = &drm_vm_shm_lock_ops;
+		else {
+			vma->vm_ops = &drm_vm_shm_ops;
+			vma->vm_private_data = (void *) map;
+		}
+
+		/* Don't let this area swap.  Change when
+		 * DRM_KERNEL advisory is supported.
+		 */
+		vma->vm_flags |= VM_LOCKED;
+		break;
+	default:
+		return -EINVAL;	/* This should never happen. */
+	};
+
+	vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */
+
+	vma->vm_file = filp; /* Needed for drm_vm_open() */
+	drm_vm_open(vma);
+	return 0;
+}
+
+static drm_map_t *ffb_find_map(struct file *filp, unsigned long off)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev;
+	drm_map_t	*map;
+	int		i;
+
+	if (!priv || (dev = priv->dev) == NULL)
+		return NULL;
+
+	for (i = 0; i < dev->map_count; i++) {
+		unsigned long uoff;
+
+		map = dev->maplist[i];
+
+		/* Ok, a little hack to make 32-bit apps work. */
+		uoff = (map->offset & 0xffffffff);
+		if (uoff == off)
+			return map;
+	}
+	return NULL;
+}
+
+static unsigned long ffb_get_unmapped_area(struct file *filp, unsigned long hint, unsigned long len, unsigned long pgoff, unsigned long flags)
+{
+	drm_map_t *map = ffb_find_map(filp, pgoff << PAGE_SHIFT);
+	unsigned long addr = -ENOMEM;
+
+	if (!map)
+		return get_unmapped_area(NULL, hint, len, pgoff, flags);
+
+	if (map->type == _DRM_FRAME_BUFFER ||
+	    map->type == _DRM_REGISTERS) {
+#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
+		addr = get_fb_unmapped_area(filp, hint, len, pgoff, flags);
+#else
+		addr = get_unmapped_area(NULL, hint, len, pgoff, flags);
+#endif
+	} else if (map->type == _DRM_SHM && SHMLBA > PAGE_SIZE) {
+		unsigned long slack = SHMLBA - PAGE_SIZE;
+
+		addr = get_unmapped_area(NULL, hint, len + slack, pgoff, flags);
+		if (!(addr & ~PAGE_MASK)) {
+			unsigned long kvirt = (unsigned long) map->handle;
+
+			if ((kvirt & (SHMLBA - 1)) != (addr & (SHMLBA - 1))) {
+				unsigned long koff, aoff;
+
+				koff = kvirt & (SHMLBA - 1);
+				aoff = addr & (SHMLBA - 1);
+				if (koff < aoff)
+					koff += SHMLBA;
+
+				addr += (koff - aoff);
+			}
+		}
+	} else {
+		addr = get_unmapped_area(NULL, hint, len, pgoff, flags);
+	}
+
+	return addr;
+}
+
+module_init(ffb_init);
+module_exit(ffb_cleanup);
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/ffb_drv.h linux/drivers/char/drm-4.0/ffb_drv.h
--- linux-2.4.18-pre2/drivers/char/drm-4.0/ffb_drv.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/ffb_drv.h	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,276 @@
+/* $Id: ffb_drv.h,v 1.1 2002/01/09 21:56:59 marcelo Exp $
+ * ffb_drv.h: Creator/Creator3D direct rendering driver.
+ *
+ * Copyright (C) 2000 David S. Miller (davem@redhat.com)
+ */
+
+/* Auxilliary clips. */
+typedef struct  {
+	volatile unsigned int min;
+	volatile unsigned int max;
+} ffb_auxclip, *ffb_auxclipPtr;
+
+/* FFB register set. */
+typedef struct _ffb_fbc {
+	/* Next vertex registers, on the right we list which drawops
+	 * use said register and the logical name the register has in
+	 * that context.
+	 */					/* DESCRIPTION		DRAWOP(NAME)	*/
+/*0x00*/unsigned int		pad1[3];	/* Reserved				*/
+/*0x0c*/volatile unsigned int	alpha;		/* ALPHA Transparency			*/
+/*0x10*/volatile unsigned int	red;		/* RED					*/
+/*0x14*/volatile unsigned int	green;		/* GREEN				*/
+/*0x18*/volatile unsigned int	blue;		/* BLUE					*/
+/*0x1c*/volatile unsigned int	z;		/* DEPTH				*/
+/*0x20*/volatile unsigned int	y;		/* Y			triangle(DOYF)	*/
+						/*                      aadot(DYF)	*/
+						/*                      ddline(DYF)	*/
+						/*                      aaline(DYF)	*/
+/*0x24*/volatile unsigned int	x;		/* X			triangle(DOXF)	*/
+						/*                      aadot(DXF)	*/
+						/*                      ddline(DXF)	*/
+						/*                      aaline(DXF)	*/
+/*0x28*/unsigned int		pad2[2];	/* Reserved				*/
+/*0x30*/volatile unsigned int	ryf;		/* Y (alias to DOYF)	ddline(RYF)	*/
+						/*			aaline(RYF)	*/
+						/*			triangle(RYF)	*/
+/*0x34*/volatile unsigned int	rxf;		/* X			ddline(RXF)	*/
+						/*			aaline(RXF)	*/
+						/*			triangle(RXF)	*/
+/*0x38*/unsigned int		pad3[2];	/* Reserved				*/
+/*0x40*/volatile unsigned int	dmyf;		/* Y (alias to DOYF)	triangle(DMYF)	*/
+/*0x44*/volatile unsigned int	dmxf;		/* X			triangle(DMXF)	*/
+/*0x48*/unsigned int		pad4[2];	/* Reserved				*/
+/*0x50*/volatile unsigned int	ebyi;		/* Y (alias to RYI)	polygon(EBYI)	*/
+/*0x54*/volatile unsigned int	ebxi;		/* X			polygon(EBXI)	*/
+/*0x58*/unsigned int		pad5[2];	/* Reserved				*/
+/*0x60*/volatile unsigned int	by;		/* Y			brline(RYI)	*/
+						/*			fastfill(OP)	*/
+						/*			polygon(YI)	*/
+						/*			rectangle(YI)	*/
+						/*			bcopy(SRCY)	*/
+						/*			vscroll(SRCY)	*/
+/*0x64*/volatile unsigned int	bx;		/* X			brline(RXI)	*/
+						/*			polygon(XI)	*/
+						/*			rectangle(XI)	*/
+						/*			bcopy(SRCX)	*/
+						/*			vscroll(SRCX)	*/
+						/*			fastfill(GO)	*/
+/*0x68*/volatile unsigned int	dy;		/* destination Y	fastfill(DSTY)	*/
+						/*			bcopy(DSRY)	*/
+						/*			vscroll(DSRY)	*/
+/*0x6c*/volatile unsigned int	dx;		/* destination X	fastfill(DSTX)	*/
+						/*			bcopy(DSTX)	*/
+						/*			vscroll(DSTX)	*/
+/*0x70*/volatile unsigned int	bh;		/* Y (alias to RYI)	brline(DYI)	*/
+						/*			dot(DYI)	*/
+						/*			polygon(ETYI)	*/
+						/* Height		fastfill(H)	*/
+						/*			bcopy(H)	*/
+						/*			vscroll(H)	*/
+						/* Y count		fastfill(NY)	*/
+/*0x74*/volatile unsigned int	bw;		/* X			dot(DXI)	*/
+						/*			brline(DXI)	*/
+						/*			polygon(ETXI)	*/
+						/*			fastfill(W)	*/
+						/*			bcopy(W)	*/
+						/*			vscroll(W)	*/
+						/*			fastfill(NX)	*/
+/*0x78*/unsigned int		pad6[2];	/* Reserved				*/
+/*0x80*/unsigned int		pad7[32];	/* Reserved				*/
+	
+	/* Setup Unit's vertex state register */
+/*100*/	volatile unsigned int	suvtx;
+/*104*/	unsigned int		pad8[63];	/* Reserved				*/
+	
+	/* Frame Buffer Control Registers */
+/*200*/	volatile unsigned int	ppc;		/* Pixel Processor Control		*/
+/*204*/	volatile unsigned int	wid;		/* Current WID				*/
+/*208*/	volatile unsigned int	fg;		/* FG data				*/
+/*20c*/	volatile unsigned int	bg;		/* BG data				*/
+/*210*/	volatile unsigned int	consty;		/* Constant Y				*/
+/*214*/	volatile unsigned int	constz;		/* Constant Z				*/
+/*218*/	volatile unsigned int	xclip;		/* X Clip				*/
+/*21c*/	volatile unsigned int	dcss;		/* Depth Cue Scale Slope		*/
+/*220*/	volatile unsigned int	vclipmin;	/* Viewclip XY Min Bounds		*/
+/*224*/	volatile unsigned int	vclipmax;	/* Viewclip XY Max Bounds		*/
+/*228*/	volatile unsigned int	vclipzmin;	/* Viewclip Z Min Bounds		*/
+/*22c*/	volatile unsigned int	vclipzmax;	/* Viewclip Z Max Bounds		*/
+/*230*/	volatile unsigned int	dcsf;		/* Depth Cue Scale Front Bound		*/
+/*234*/	volatile unsigned int	dcsb;		/* Depth Cue Scale Back Bound		*/
+/*238*/	volatile unsigned int	dczf;		/* Depth Cue Z Front			*/
+/*23c*/	volatile unsigned int	dczb;		/* Depth Cue Z Back			*/
+/*240*/	unsigned int		pad9;		/* Reserved				*/
+/*244*/	volatile unsigned int	blendc;		/* Alpha Blend Control			*/
+/*248*/	volatile unsigned int	blendc1;	/* Alpha Blend Color 1			*/
+/*24c*/	volatile unsigned int	blendc2;	/* Alpha Blend Color 2			*/
+/*250*/	volatile unsigned int	fbramitc;	/* FB RAM Interleave Test Control	*/
+/*254*/	volatile unsigned int	fbc;		/* Frame Buffer Control			*/
+/*258*/	volatile unsigned int	rop;		/* Raster OPeration			*/
+/*25c*/	volatile unsigned int	cmp;		/* Frame Buffer Compare			*/
+/*260*/	volatile unsigned int	matchab;	/* Buffer AB Match Mask			*/
+/*264*/	volatile unsigned int	matchc;		/* Buffer C(YZ) Match Mask		*/
+/*268*/	volatile unsigned int	magnab;		/* Buffer AB Magnitude Mask		*/
+/*26c*/	volatile unsigned int	magnc;		/* Buffer C(YZ) Magnitude Mask		*/
+/*270*/	volatile unsigned int	fbcfg0;		/* Frame Buffer Config 0		*/
+/*274*/	volatile unsigned int	fbcfg1;		/* Frame Buffer Config 1		*/
+/*278*/	volatile unsigned int	fbcfg2;		/* Frame Buffer Config 2		*/
+/*27c*/	volatile unsigned int	fbcfg3;		/* Frame Buffer Config 3		*/
+/*280*/	volatile unsigned int	ppcfg;		/* Pixel Processor Config		*/
+/*284*/	volatile unsigned int	pick;		/* Picking Control			*/
+/*288*/	volatile unsigned int	fillmode;	/* FillMode				*/
+/*28c*/	volatile unsigned int	fbramwac;	/* FB RAM Write Address Control		*/
+/*290*/	volatile unsigned int	pmask;		/* RGB PlaneMask			*/
+/*294*/	volatile unsigned int	xpmask;		/* X PlaneMask				*/
+/*298*/	volatile unsigned int	ypmask;		/* Y PlaneMask				*/
+/*29c*/	volatile unsigned int	zpmask;		/* Z PlaneMask				*/
+/*2a0*/	ffb_auxclip		auxclip[4]; 	/* Auxilliary Viewport Clip		*/
+	
+	/* New 3dRAM III support regs */
+/*2c0*/	volatile unsigned int	rawblend2;
+/*2c4*/	volatile unsigned int	rawpreblend;
+/*2c8*/	volatile unsigned int	rawstencil;
+/*2cc*/	volatile unsigned int	rawstencilctl;
+/*2d0*/	volatile unsigned int	threedram1;
+/*2d4*/	volatile unsigned int	threedram2;
+/*2d8*/	volatile unsigned int	passin;
+/*2dc*/	volatile unsigned int	rawclrdepth;
+/*2e0*/	volatile unsigned int	rawpmask;
+/*2e4*/	volatile unsigned int	rawcsrc;
+/*2e8*/	volatile unsigned int	rawmatch;
+/*2ec*/	volatile unsigned int	rawmagn;
+/*2f0*/	volatile unsigned int	rawropblend;
+/*2f4*/	volatile unsigned int	rawcmp;
+/*2f8*/	volatile unsigned int	rawwac;
+/*2fc*/	volatile unsigned int	fbramid;
+	
+/*300*/	volatile unsigned int	drawop;		/* Draw OPeration			*/
+/*304*/	unsigned int		pad10[2];	/* Reserved				*/
+/*30c*/	volatile unsigned int	lpat;		/* Line Pattern control			*/
+/*310*/	unsigned int		pad11;		/* Reserved				*/
+/*314*/	volatile unsigned int	fontxy;		/* XY Font coordinate			*/
+/*318*/	volatile unsigned int	fontw;		/* Font Width				*/
+/*31c*/	volatile unsigned int	fontinc;	/* Font Increment			*/
+/*320*/	volatile unsigned int	font;		/* Font bits				*/
+/*324*/	unsigned int		pad12[3];	/* Reserved				*/
+/*330*/	volatile unsigned int	blend2;
+/*334*/	volatile unsigned int	preblend;
+/*338*/	volatile unsigned int	stencil;
+/*33c*/	volatile unsigned int	stencilctl;
+
+/*340*/	unsigned int		pad13[4];	/* Reserved				*/
+/*350*/	volatile unsigned int	dcss1;		/* Depth Cue Scale Slope 1		*/
+/*354*/	volatile unsigned int	dcss2;		/* Depth Cue Scale Slope 2		*/
+/*358*/	volatile unsigned int	dcss3;		/* Depth Cue Scale Slope 3		*/
+/*35c*/	volatile unsigned int	widpmask;
+/*360*/	volatile unsigned int	dcs2;
+/*364*/	volatile unsigned int	dcs3;
+/*368*/	volatile unsigned int	dcs4;
+/*36c*/	unsigned int		pad14;		/* Reserved				*/
+/*370*/	volatile unsigned int	dcd2;
+/*374*/	volatile unsigned int	dcd3;
+/*378*/	volatile unsigned int	dcd4;
+/*37c*/	unsigned int		pad15;		/* Reserved				*/
+/*380*/	volatile unsigned int	pattern[32];	/* area Pattern				*/
+/*400*/	unsigned int		pad16[8];	/* Reserved				*/
+/*420*/	volatile unsigned int	reset;		/* chip RESET				*/
+/*424*/	unsigned int		pad17[247];	/* Reserved				*/
+/*800*/	volatile unsigned int	devid;		/* Device ID				*/
+/*804*/	unsigned int		pad18[63];	/* Reserved				*/
+/*900*/	volatile unsigned int	ucsr;		/* User Control & Status Register	*/
+/*904*/	unsigned int		pad19[31];	/* Reserved				*/
+/*980*/	volatile unsigned int	mer;		/* Mode Enable Register			*/
+/*984*/	unsigned int		pad20[1439];	/* Reserved				*/
+} ffb_fbc, *ffb_fbcPtr;
+
+struct ffb_hw_context {
+	int is_2d_only;
+
+	unsigned int ppc;
+	unsigned int wid;
+	unsigned int fg;
+	unsigned int bg;
+	unsigned int consty;
+	unsigned int constz;
+	unsigned int xclip;
+	unsigned int dcss;
+	unsigned int vclipmin;
+	unsigned int vclipmax;
+	unsigned int vclipzmin;
+	unsigned int vclipzmax;
+	unsigned int dcsf;
+	unsigned int dcsb;
+	unsigned int dczf;
+	unsigned int dczb;
+	unsigned int blendc;
+	unsigned int blendc1;
+	unsigned int blendc2;
+	unsigned int fbc;
+	unsigned int rop;
+	unsigned int cmp;
+	unsigned int matchab;
+	unsigned int matchc;
+	unsigned int magnab;
+	unsigned int magnc;
+	unsigned int pmask;
+	unsigned int xpmask;
+	unsigned int ypmask;
+	unsigned int zpmask;
+	unsigned int auxclip0min;
+	unsigned int auxclip0max;
+	unsigned int auxclip1min;
+	unsigned int auxclip1max;
+	unsigned int auxclip2min;
+	unsigned int auxclip2max;
+	unsigned int auxclip3min;
+	unsigned int auxclip3max;
+	unsigned int drawop;
+	unsigned int lpat;
+	unsigned int fontxy;
+	unsigned int fontw;
+	unsigned int fontinc;
+	unsigned int area_pattern[32];
+	unsigned int ucsr;
+	unsigned int stencil;
+	unsigned int stencilctl;
+	unsigned int dcss1;
+	unsigned int dcss2;
+	unsigned int dcss3;
+	unsigned int dcs2;
+	unsigned int dcs3;
+	unsigned int dcs4;
+	unsigned int dcd2;
+	unsigned int dcd3;
+	unsigned int dcd4;
+	unsigned int mer;
+};
+
+#define FFB_MAX_CTXS	32
+
+enum ffb_chip_type {
+	ffb1_prototype = 0,	/* Early pre-FCS FFB */
+	ffb1_standard,		/* First FCS FFB, 100Mhz UPA, 66MHz gclk */
+	ffb1_speedsort,		/* Second FCS FFB, 100Mhz UPA, 75MHz gclk */
+	ffb2_prototype,		/* Early pre-FCS vertical FFB2 */
+	ffb2_vertical,		/* First FCS FFB2/vertical, 100Mhz UPA, 100MHZ gclk,
+				   75(SingleBuffer)/83(DoubleBuffer) MHz fclk */
+	ffb2_vertical_plus,	/* Second FCS FFB2/vertical, same timings */
+	ffb2_horizontal,	/* First FCS FFB2/horizontal, same timings as FFB2/vert */
+	ffb2_horizontal_plus,	/* Second FCS FFB2/horizontal, same timings */
+	afb_m3,			/* FCS Elite3D, 3 float chips */
+	afb_m6			/* FCS Elite3D, 6 float chips */
+};
+
+typedef struct ffb_dev_priv {
+	/* Misc software state. */
+	int			prom_node;
+	enum ffb_chip_type	ffb_type;
+	u64			card_phys_base;
+	struct miscdevice 	miscdev;
+
+	/* Controller registers. */
+	ffb_fbcPtr		regs;
+
+	/* Context table. */
+	struct ffb_hw_context	*hw_state[FFB_MAX_CTXS];
+} ffb_dev_priv_t;
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/fops.c linux/drivers/char/drm-4.0/fops.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/fops.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/fops.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,253 @@
+/* fops.c -- File operations for DRM -*- linux-c -*-
+ * Created: Mon Jan  4 08:58:31 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *    Daryll Strauss <daryll@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include <linux/poll.h>
+
+/* drm_open is called whenever a process opens /dev/drm. */
+
+int drm_open_helper(struct inode *inode, struct file *filp, drm_device_t *dev)
+{
+	kdev_t	     minor = MINOR(inode->i_rdev);
+	drm_file_t   *priv;
+
+	if (filp->f_flags & O_EXCL)   return -EBUSY; /* No exclusive opens */
+	if (!drm_cpu_valid())         return -EINVAL;
+
+	DRM_DEBUG("pid = %d, minor = %d\n", current->pid, minor);
+
+	priv		    = drm_alloc(sizeof(*priv), DRM_MEM_FILES);
+	if(priv == NULL)
+		return -ENOMEM;
+	memset(priv, 0, sizeof(*priv));
+
+	filp->private_data  = priv;
+	priv->uid	    = current->euid;
+	priv->pid	    = current->pid;
+	priv->minor	    = minor;
+	priv->dev	    = dev;
+	priv->ioctl_count   = 0;
+	priv->authenticated = capable(CAP_SYS_ADMIN);
+
+	down(&dev->struct_sem);
+	if (!dev->file_last) {
+		priv->next	= NULL;
+		priv->prev	= NULL;
+		dev->file_first = priv;
+		dev->file_last	= priv;
+	} else {
+		priv->next	     = NULL;
+		priv->prev	     = dev->file_last;
+		dev->file_last->next = priv;
+		dev->file_last	     = priv;
+	}
+	up(&dev->struct_sem);
+	
+	return 0;
+}
+
+int drm_flush(struct file *filp)
+{
+	drm_file_t    *priv   = filp->private_data;
+	drm_device_t  *dev    = priv->dev;
+
+	DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n",
+		  current->pid, dev->device, dev->open_count);
+	return 0;
+}
+
+/* drm_release is called whenever a process closes /dev/drm*.  Linux calls
+   this only if any mappings have been closed. */
+
+int drm_release(struct inode *inode, struct file *filp)
+{
+	drm_file_t    *priv   = filp->private_data;
+	drm_device_t  *dev    = priv->dev;
+
+	DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n",
+		  current->pid, dev->device, dev->open_count);
+
+	if (dev->lock.hw_lock
+	    && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)
+	    && dev->lock.pid == current->pid) {
+		DRM_ERROR("Process %d dead, freeing lock for context %d\n",
+			  current->pid,
+			  _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+		drm_lock_free(dev,
+			      &dev->lock.hw_lock->lock,
+			      _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+		
+				/* FIXME: may require heavy-handed reset of
+                                   hardware at this point, possibly
+                                   processed via a callback to the X
+                                   server. */
+	}
+	drm_reclaim_buffers(dev, priv->pid);
+
+	drm_fasync(-1, filp, 0);
+
+	down(&dev->struct_sem);
+	if (priv->prev) priv->prev->next = priv->next;
+	else		dev->file_first	 = priv->next;
+	if (priv->next) priv->next->prev = priv->prev;
+	else		dev->file_last	 = priv->prev;
+	up(&dev->struct_sem);
+	
+	drm_free(priv, sizeof(*priv), DRM_MEM_FILES);
+	
+	return 0;
+}
+
+int drm_fasync(int fd, struct file *filp, int on)
+{
+	drm_file_t    *priv   = filp->private_data;
+	drm_device_t  *dev    = priv->dev;
+	int	      retcode;
+	
+	DRM_DEBUG("fd = %d, device = 0x%x\n", fd, dev->device);
+	retcode = fasync_helper(fd, filp, on, &dev->buf_async);
+	if (retcode < 0) return retcode;
+	return 0;
+}
+
+
+/* The drm_read and drm_write_string code (especially that which manages
+   the circular buffer), is based on Alessandro Rubini's LINUX DEVICE
+   DRIVERS (Cambridge: O'Reilly, 1998), pages 111-113. */
+
+ssize_t drm_read(struct file *filp, char *buf, size_t count, loff_t *off)
+{
+	drm_file_t    *priv   = filp->private_data;
+	drm_device_t  *dev    = priv->dev;
+	int	      left;
+	int	      avail;
+	int	      send;
+	int	      cur;
+
+	DRM_DEBUG("%p, %p\n", dev->buf_rp, dev->buf_wp);
+	
+	while (dev->buf_rp == dev->buf_wp) {
+		DRM_DEBUG("  sleeping\n");
+		if (filp->f_flags & O_NONBLOCK) {
+			return -EAGAIN;
+		}
+		interruptible_sleep_on(&dev->buf_readers);
+		if (signal_pending(current)) {
+			DRM_DEBUG("  interrupted\n");
+			return -ERESTARTSYS;
+		}
+		DRM_DEBUG("  awake\n");
+	}
+
+	left  = (dev->buf_rp + DRM_BSZ - dev->buf_wp) % DRM_BSZ;
+	avail = DRM_BSZ - left;
+	send  = DRM_MIN(avail, count);
+
+	while (send) {
+		if (dev->buf_wp > dev->buf_rp) {
+			cur = DRM_MIN(send, dev->buf_wp - dev->buf_rp);
+		} else {
+			cur = DRM_MIN(send, dev->buf_end - dev->buf_rp);
+		}
+		if (copy_to_user(buf, dev->buf_rp, cur))
+			return -EFAULT;
+		dev->buf_rp += cur;
+		if (dev->buf_rp == dev->buf_end) dev->buf_rp = dev->buf;
+		send -= cur;
+	}
+	
+	wake_up_interruptible(&dev->buf_writers);
+	return DRM_MIN(avail, count);;
+}
+
+int drm_write_string(drm_device_t *dev, const char *s)
+{
+	int left   = (dev->buf_rp + DRM_BSZ - dev->buf_wp) % DRM_BSZ;
+	int send   = strlen(s);
+	int count;
+
+	DRM_DEBUG("%d left, %d to send (%p, %p)\n",
+		  left, send, dev->buf_rp, dev->buf_wp);
+	
+	if (left == 1 || dev->buf_wp != dev->buf_rp) {
+		DRM_ERROR("Buffer not empty (%d left, wp = %p, rp = %p)\n",
+			  left,
+			  dev->buf_wp,
+			  dev->buf_rp);
+	}
+
+	while (send) {
+		if (dev->buf_wp >= dev->buf_rp) {
+			count = DRM_MIN(send, dev->buf_end - dev->buf_wp);
+			if (count == left) --count; /* Leave a hole */
+		} else {
+			count = DRM_MIN(send, dev->buf_rp - dev->buf_wp - 1);
+		}
+		strncpy(dev->buf_wp, s, count);
+		dev->buf_wp += count;
+		if (dev->buf_wp == dev->buf_end) dev->buf_wp = dev->buf;
+		send -= count;
+	}
+
+#if LINUX_VERSION_CODE < 0x020315 && !defined(KILLFASYNCHASTHREEPARAMETERS)
+	/* The extra parameter to kill_fasync was added in 2.3.21, and is
+           _not_ present in _stock_ 2.2.14 and 2.2.15.  However, some
+           distributions patch 2.2.x kernels to add this parameter.  The
+           Makefile.linux attempts to detect this addition and defines
+           KILLFASYNCHASTHREEPARAMETERS if three parameters are found. */
+	if (dev->buf_async) kill_fasync(dev->buf_async, SIGIO);
+#else
+
+				/* Parameter added in 2.3.21. */
+#if LINUX_VERSION_CODE < 0x020400
+	if (dev->buf_async) kill_fasync(dev->buf_async, SIGIO, POLL_IN);
+#else
+				/* Type of first parameter changed in
+                                   Linux 2.4.0-test2... */
+	if (dev->buf_async) kill_fasync(&dev->buf_async, SIGIO, POLL_IN);
+#endif
+#endif
+	DRM_DEBUG("waking\n");
+	wake_up_interruptible(&dev->buf_readers);
+	return 0;
+}
+
+unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait)
+{
+	drm_file_t   *priv = filp->private_data;
+	drm_device_t *dev  = priv->dev;
+
+	poll_wait(filp, &dev->buf_readers, wait);
+	if (dev->buf_wp != dev->buf_rp) return POLLIN | POLLRDNORM;
+	return 0;
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/gamma_dma.c linux/drivers/char/drm-4.0/gamma_dma.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/gamma_dma.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/gamma_dma.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,836 @@
+/* gamma_dma.c -- DMA support for GMX 2000 -*- linux-c -*-
+ * Created: Fri Mar 19 14:30:16 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "gamma_drv.h"
+
+#include <linux/interrupt.h>	/* For task queue support */
+
+
+/* WARNING!!! MAGIC NUMBER!!!  The number of regions already added to the
+   kernel must be specified here.  Currently, the number is 2.	This must
+   match the order the X server uses for instantiating register regions ,
+   or must be passed in a new ioctl. */
+#define GAMMA_REG(reg)						   \
+	(2							   \
+	 + ((reg < 0x1000)					   \
+	    ? 0							   \
+	    : ((reg < 0x10000) ? 1 : ((reg < 0x11000) ? 2 : 3))))
+
+#define GAMMA_OFF(reg)						   \
+	((reg < 0x1000)						   \
+	 ? reg							   \
+	 : ((reg < 0x10000)					   \
+	    ? (reg - 0x1000)					   \
+	    : ((reg < 0x11000)					   \
+	       ? (reg - 0x10000)				   \
+	       : (reg - 0x11000))))
+
+#define GAMMA_BASE(reg)	 ((unsigned long)dev->maplist[GAMMA_REG(reg)]->handle)
+#define GAMMA_ADDR(reg)	 (GAMMA_BASE(reg) + GAMMA_OFF(reg))
+#define GAMMA_DEREF(reg) *(__volatile__ int *)GAMMA_ADDR(reg)
+#define GAMMA_READ(reg)	 GAMMA_DEREF(reg)
+#define GAMMA_WRITE(reg,val) do { GAMMA_DEREF(reg) = val; } while (0)
+
+#define GAMMA_BROADCASTMASK    0x9378
+#define GAMMA_COMMANDINTENABLE 0x0c48
+#define GAMMA_DMAADDRESS       0x0028
+#define GAMMA_DMACOUNT	       0x0030
+#define GAMMA_FILTERMODE       0x8c00
+#define GAMMA_GCOMMANDINTFLAGS 0x0c50
+#define GAMMA_GCOMMANDMODE     0x0c40
+#define GAMMA_GCOMMANDSTATUS   0x0c60
+#define GAMMA_GDELAYTIMER      0x0c38
+#define GAMMA_GDMACONTROL      0x0060
+#define GAMMA_GINTENABLE       0x0808
+#define GAMMA_GINTFLAGS	       0x0810
+#define GAMMA_INFIFOSPACE      0x0018
+#define GAMMA_OUTFIFOWORDS     0x0020
+#define GAMMA_OUTPUTFIFO       0x2000
+#define GAMMA_SYNC	       0x8c40
+#define GAMMA_SYNC_TAG	       0x0188
+
+static inline void gamma_dma_dispatch(drm_device_t *dev, unsigned long address,
+				      unsigned long length)
+{
+	GAMMA_WRITE(GAMMA_DMAADDRESS, virt_to_phys((void *)address));
+	while (GAMMA_READ(GAMMA_GCOMMANDSTATUS) != 4)
+		;
+	GAMMA_WRITE(GAMMA_DMACOUNT, length / 4);
+}
+
+static inline void gamma_dma_quiescent_single(drm_device_t *dev)
+{
+	while (GAMMA_READ(GAMMA_DMACOUNT))
+		;
+	while (GAMMA_READ(GAMMA_INFIFOSPACE) < 3)
+		;
+
+	GAMMA_WRITE(GAMMA_FILTERMODE, 1 << 10);
+	GAMMA_WRITE(GAMMA_SYNC, 0);
+	
+	do {
+		while (!GAMMA_READ(GAMMA_OUTFIFOWORDS))
+			;
+	} while (GAMMA_READ(GAMMA_OUTPUTFIFO) != GAMMA_SYNC_TAG);
+}
+
+static inline void gamma_dma_quiescent_dual(drm_device_t *dev)
+{
+	while (GAMMA_READ(GAMMA_DMACOUNT))
+		;
+	while (GAMMA_READ(GAMMA_INFIFOSPACE) < 3)
+		;
+
+	GAMMA_WRITE(GAMMA_BROADCASTMASK, 3);
+
+	GAMMA_WRITE(GAMMA_FILTERMODE, 1 << 10);
+	GAMMA_WRITE(GAMMA_SYNC, 0);
+	
+				/* Read from first MX */
+	do {
+		while (!GAMMA_READ(GAMMA_OUTFIFOWORDS))
+			;
+	} while (GAMMA_READ(GAMMA_OUTPUTFIFO) != GAMMA_SYNC_TAG);
+	
+				/* Read from second MX */
+	do {
+		while (!GAMMA_READ(GAMMA_OUTFIFOWORDS + 0x10000))
+			;
+	} while (GAMMA_READ(GAMMA_OUTPUTFIFO + 0x10000) != GAMMA_SYNC_TAG);
+}
+
+static inline void gamma_dma_ready(drm_device_t *dev)
+{
+	while (GAMMA_READ(GAMMA_DMACOUNT))
+		;
+}
+
+static inline int gamma_dma_is_ready(drm_device_t *dev)
+{
+	return !GAMMA_READ(GAMMA_DMACOUNT);
+}
+
+static void gamma_dma_service(int irq, void *device, struct pt_regs *regs)
+{
+	drm_device_t	 *dev = (drm_device_t *)device;
+	drm_device_dma_t *dma = dev->dma;
+	
+	atomic_inc(&dev->total_irq);
+	GAMMA_WRITE(GAMMA_GDELAYTIMER, 0xc350/2); /* 0x05S */
+	GAMMA_WRITE(GAMMA_GCOMMANDINTFLAGS, 8);
+	GAMMA_WRITE(GAMMA_GINTFLAGS, 0x2001);
+	if (gamma_dma_is_ready(dev)) {
+				/* Free previous buffer */
+		if (test_and_set_bit(0, &dev->dma_flag)) {
+			atomic_inc(&dma->total_missed_free);
+			return;
+		}
+		if (dma->this_buffer) {
+			drm_free_buffer(dev, dma->this_buffer);
+			dma->this_buffer = NULL;
+		}
+		clear_bit(0, &dev->dma_flag);
+
+				/* Dispatch new buffer */
+		queue_task(&dev->tq, &tq_immediate);
+		mark_bh(IMMEDIATE_BH);
+	}
+}
+
+/* Only called by gamma_dma_schedule. */
+static int gamma_do_dma(drm_device_t *dev, int locked)
+{
+	unsigned long	 address;
+	unsigned long	 length;
+	drm_buf_t	 *buf;
+	int		 retcode = 0;
+	drm_device_dma_t *dma = dev->dma;
+#if DRM_DMA_HISTOGRAM
+	cycles_t	 dma_start, dma_stop;
+#endif
+
+	if (test_and_set_bit(0, &dev->dma_flag)) {
+		atomic_inc(&dma->total_missed_dma);
+		return -EBUSY;
+	}
+	
+#if DRM_DMA_HISTOGRAM
+	dma_start = get_cycles();
+#endif
+
+	if (!dma->next_buffer) {
+		DRM_ERROR("No next_buffer\n");
+		clear_bit(0, &dev->dma_flag);
+		return -EINVAL;
+	}
+
+	buf	= dma->next_buffer;
+	address = (unsigned long)buf->address;
+	length	= buf->used;
+
+	DRM_DEBUG("context %d, buffer %d (%ld bytes)\n",
+		  buf->context, buf->idx, length);
+
+	if (buf->list == DRM_LIST_RECLAIM) {
+		drm_clear_next_buffer(dev);
+		drm_free_buffer(dev, buf);
+		clear_bit(0, &dev->dma_flag);
+		return -EINVAL;
+	}
+
+	if (!length) {
+		DRM_ERROR("0 length buffer\n");
+		drm_clear_next_buffer(dev);
+		drm_free_buffer(dev, buf);
+		clear_bit(0, &dev->dma_flag);
+		return 0;
+	}
+	
+	if (!gamma_dma_is_ready(dev)) {
+		clear_bit(0, &dev->dma_flag);
+		return -EBUSY;
+	}
+
+	if (buf->while_locked) {
+		if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+			DRM_ERROR("Dispatching buffer %d from pid %d"
+				  " \"while locked\", but no lock held\n",
+				  buf->idx, buf->pid);
+		}
+	} else {
+		if (!locked && !drm_lock_take(&dev->lock.hw_lock->lock,
+					      DRM_KERNEL_CONTEXT)) {
+			atomic_inc(&dma->total_missed_lock);
+			clear_bit(0, &dev->dma_flag);
+			return -EBUSY;
+		}
+	}
+
+	if (dev->last_context != buf->context
+	    && !(dev->queuelist[buf->context]->flags
+		 & _DRM_CONTEXT_PRESERVED)) {
+				/* PRE: dev->last_context != buf->context */
+		if (drm_context_switch(dev, dev->last_context, buf->context)) {
+			drm_clear_next_buffer(dev);
+			drm_free_buffer(dev, buf);
+		}
+		retcode = -EBUSY;
+		goto cleanup;
+			
+				/* POST: we will wait for the context
+				   switch and will dispatch on a later call
+				   when dev->last_context == buf->context.
+				   NOTE WE HOLD THE LOCK THROUGHOUT THIS
+				   TIME! */
+	}
+
+	drm_clear_next_buffer(dev);
+	buf->pending	 = 1;
+	buf->waiting	 = 0;
+	buf->list	 = DRM_LIST_PEND;
+#if DRM_DMA_HISTOGRAM
+	buf->time_dispatched = get_cycles();
+#endif
+
+	gamma_dma_dispatch(dev, address, length);
+	drm_free_buffer(dev, dma->this_buffer);
+	dma->this_buffer = buf;
+
+	atomic_add(length, &dma->total_bytes);
+	atomic_inc(&dma->total_dmas);
+
+	if (!buf->while_locked && !dev->context_flag && !locked) {
+		if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				  DRM_KERNEL_CONTEXT)) {
+			DRM_ERROR("\n");
+		}
+	}
+cleanup:
+
+	clear_bit(0, &dev->dma_flag);
+
+#if DRM_DMA_HISTOGRAM
+	dma_stop = get_cycles();
+	atomic_inc(&dev->histo.dma[drm_histogram_slot(dma_stop - dma_start)]);
+#endif
+
+	return retcode;
+}
+
+static void gamma_dma_schedule_timer_wrapper(unsigned long dev)
+{
+	gamma_dma_schedule((drm_device_t *)dev, 0);
+}
+
+static void gamma_dma_schedule_tq_wrapper(void *dev)
+{
+	gamma_dma_schedule(dev, 0);
+}
+
+int gamma_dma_schedule(drm_device_t *dev, int locked)
+{
+	int		 next;
+	drm_queue_t	 *q;
+	drm_buf_t	 *buf;
+	int		 retcode   = 0;
+	int		 processed = 0;
+	int		 missed;
+	int		 expire	   = 20;
+	drm_device_dma_t *dma	   = dev->dma;
+#if DRM_DMA_HISTOGRAM
+	cycles_t	 schedule_start;
+#endif
+
+	if (test_and_set_bit(0, &dev->interrupt_flag)) {
+				/* Not reentrant */
+		atomic_inc(&dma->total_missed_sched);
+		return -EBUSY;
+	}
+	missed = atomic_read(&dma->total_missed_sched);
+
+#if DRM_DMA_HISTOGRAM
+	schedule_start = get_cycles();
+#endif
+
+again:
+	if (dev->context_flag) {
+		clear_bit(0, &dev->interrupt_flag);
+		return -EBUSY;
+	}
+	if (dma->next_buffer) {
+				/* Unsent buffer that was previously
+				   selected, but that couldn't be sent
+				   because the lock could not be obtained
+				   or the DMA engine wasn't ready.  Try
+				   again. */
+		atomic_inc(&dma->total_tried);
+		if (!(retcode = gamma_do_dma(dev, locked))) {
+			atomic_inc(&dma->total_hit);
+			++processed;
+		}
+	} else {
+		do {
+			next = drm_select_queue(dev,
+					     gamma_dma_schedule_timer_wrapper);
+			if (next >= 0) {
+				q   = dev->queuelist[next];
+				buf = drm_waitlist_get(&q->waitlist);
+				dma->next_buffer = buf;
+				dma->next_queue	 = q;
+				if (buf && buf->list == DRM_LIST_RECLAIM) {
+					drm_clear_next_buffer(dev);
+					drm_free_buffer(dev, buf);
+				}
+			}
+		} while (next >= 0 && !dma->next_buffer);
+		if (dma->next_buffer) {
+			if (!(retcode = gamma_do_dma(dev, locked))) {
+				++processed;
+			}
+		}
+	}
+
+	if (--expire) {
+		if (missed != atomic_read(&dma->total_missed_sched)) {
+			atomic_inc(&dma->total_lost);
+			if (gamma_dma_is_ready(dev)) goto again;
+		}
+		if (processed && gamma_dma_is_ready(dev)) {
+			atomic_inc(&dma->total_lost);
+			processed = 0;
+			goto again;
+		}
+	}
+	
+	clear_bit(0, &dev->interrupt_flag);
+	
+#if DRM_DMA_HISTOGRAM
+	atomic_inc(&dev->histo.schedule[drm_histogram_slot(get_cycles()
+							   - schedule_start)]);
+#endif
+	return retcode;
+}
+
+static int gamma_dma_priority(drm_device_t *dev, drm_dma_t *d)
+{
+	unsigned long	  address;
+	unsigned long	  length;
+	int		  must_free = 0;
+	int		  retcode   = 0;
+	int		  i;
+	int		  idx;
+	drm_buf_t	  *buf;
+	drm_buf_t	  *last_buf = NULL;
+	drm_device_dma_t  *dma	    = dev->dma;
+	DECLARE_WAITQUEUE(entry, current);
+
+				/* Turn off interrupt handling */
+	while (test_and_set_bit(0, &dev->interrupt_flag)) {
+		schedule();
+		if (signal_pending(current)) return -EINTR;
+	}
+	if (!(d->flags & _DRM_DMA_WHILE_LOCKED)) {
+		while (!drm_lock_take(&dev->lock.hw_lock->lock,
+				      DRM_KERNEL_CONTEXT)) {
+			schedule();
+			if (signal_pending(current)) {
+				clear_bit(0, &dev->interrupt_flag);
+				return -EINTR;
+			}
+		}
+		++must_free;
+	}
+	atomic_inc(&dma->total_prio);
+
+	for (i = 0; i < d->send_count; i++) {
+		idx = d->send_indices[i];
+		if (idx < 0 || idx >= dma->buf_count) {
+			DRM_ERROR("Index %d (of %d max)\n",
+				  d->send_indices[i], dma->buf_count - 1);
+			continue;
+		}
+		buf = dma->buflist[ idx ];
+		if (buf->pid != current->pid) {
+			DRM_ERROR("Process %d using buffer owned by %d\n",
+				  current->pid, buf->pid);
+			retcode = -EINVAL;
+			goto cleanup;
+		}
+		if (buf->list != DRM_LIST_NONE) {
+			DRM_ERROR("Process %d using %d's buffer on list %d\n",
+				  current->pid, buf->pid, buf->list);
+			retcode = -EINVAL;
+			goto cleanup;
+		}
+				/* This isn't a race condition on
+				   buf->list, since our concern is the
+				   buffer reclaim during the time the
+				   process closes the /dev/drm? handle, so
+				   it can't also be doing DMA. */
+		buf->list	  = DRM_LIST_PRIO;
+		buf->used	  = d->send_sizes[i];
+		buf->context	  = d->context;
+		buf->while_locked = d->flags & _DRM_DMA_WHILE_LOCKED;
+		address		  = (unsigned long)buf->address;
+		length		  = buf->used;
+		if (!length) {
+			DRM_ERROR("0 length buffer\n");
+		}
+		if (buf->pending) {
+			DRM_ERROR("Sending pending buffer:"
+				  " buffer %d, offset %d\n",
+				  d->send_indices[i], i);
+			retcode = -EINVAL;
+			goto cleanup;
+		}
+		if (buf->waiting) {
+			DRM_ERROR("Sending waiting buffer:"
+				  " buffer %d, offset %d\n",
+				  d->send_indices[i], i);
+			retcode = -EINVAL;
+			goto cleanup;
+		}
+		buf->pending = 1;
+		
+		if (dev->last_context != buf->context
+		    && !(dev->queuelist[buf->context]->flags
+			 & _DRM_CONTEXT_PRESERVED)) {
+			add_wait_queue(&dev->context_wait, &entry);
+			current->state = TASK_INTERRUPTIBLE;
+				/* PRE: dev->last_context != buf->context */
+			drm_context_switch(dev, dev->last_context,
+					   buf->context);
+				/* POST: we will wait for the context
+				   switch and will dispatch on a later call
+				   when dev->last_context == buf->context.
+				   NOTE WE HOLD THE LOCK THROUGHOUT THIS
+				   TIME! */
+			schedule();
+			current->state = TASK_RUNNING;
+			remove_wait_queue(&dev->context_wait, &entry);
+			if (signal_pending(current)) {
+				retcode = -EINTR;
+				goto cleanup;
+			}
+			if (dev->last_context != buf->context) {
+				DRM_ERROR("Context mismatch: %d %d\n",
+					  dev->last_context,
+					  buf->context);
+			}
+		}
+
+#if DRM_DMA_HISTOGRAM
+		buf->time_queued     = get_cycles();
+		buf->time_dispatched = buf->time_queued;
+#endif
+		gamma_dma_dispatch(dev, address, length);
+		atomic_add(length, &dma->total_bytes);
+		atomic_inc(&dma->total_dmas);
+		
+		if (last_buf) {
+			drm_free_buffer(dev, last_buf);
+		}
+		last_buf = buf;
+	}
+
+
+cleanup:
+	if (last_buf) {
+		gamma_dma_ready(dev);
+		drm_free_buffer(dev, last_buf);
+	}
+	
+	if (must_free && !dev->context_flag) {
+		if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				  DRM_KERNEL_CONTEXT)) {
+			DRM_ERROR("\n");
+		}
+	}
+	clear_bit(0, &dev->interrupt_flag);
+	return retcode;
+}
+
+static int gamma_dma_send_buffers(drm_device_t *dev, drm_dma_t *d)
+{
+	DECLARE_WAITQUEUE(entry, current);
+	drm_buf_t	  *last_buf = NULL;
+	int		  retcode   = 0;
+	drm_device_dma_t  *dma	    = dev->dma;
+
+	if (d->flags & _DRM_DMA_BLOCK) {
+		last_buf = dma->buflist[d->send_indices[d->send_count-1]];
+		add_wait_queue(&last_buf->dma_wait, &entry);
+	}
+	
+	if ((retcode = drm_dma_enqueue(dev, d))) {
+		if (d->flags & _DRM_DMA_BLOCK)
+			remove_wait_queue(&last_buf->dma_wait, &entry);
+		return retcode;
+	}
+	
+	gamma_dma_schedule(dev, 0);
+	
+	if (d->flags & _DRM_DMA_BLOCK) {
+		DRM_DEBUG("%d waiting\n", current->pid);
+		for (;;) {
+			current->state = TASK_INTERRUPTIBLE;
+			if (!last_buf->waiting && !last_buf->pending)
+				break; /* finished */
+			schedule();
+			if (signal_pending(current)) {
+				retcode = -EINTR; /* Can't restart */
+				break;
+			}
+		}
+		current->state = TASK_RUNNING;
+		DRM_DEBUG("%d running\n", current->pid);
+		remove_wait_queue(&last_buf->dma_wait, &entry);
+		if (!retcode
+		    || (last_buf->list==DRM_LIST_PEND && !last_buf->pending)) {
+			if (!waitqueue_active(&last_buf->dma_wait)) {
+				drm_free_buffer(dev, last_buf);
+			}
+		}
+		if (retcode) {
+			DRM_ERROR("ctx%d w%d p%d c%d i%d l%d %d/%d\n",
+				  d->context,
+				  last_buf->waiting,
+				  last_buf->pending,
+				  DRM_WAITCOUNT(dev, d->context),
+				  last_buf->idx,
+				  last_buf->list,
+				  last_buf->pid,
+				  current->pid);
+		}
+	}
+	return retcode;
+}
+
+int gamma_dma(struct inode *inode, struct file *filp, unsigned int cmd,
+	      unsigned long arg)
+{
+	drm_file_t	  *priv	    = filp->private_data;
+	drm_device_t	  *dev	    = priv->dev;
+	drm_device_dma_t  *dma	    = dev->dma;
+	int		  retcode   = 0;
+	drm_dma_t	  d;
+
+	if (copy_from_user(&d, (drm_dma_t *)arg, sizeof(d)))
+		return -EFAULT;
+	DRM_DEBUG("%d %d: %d send, %d req\n",
+		  current->pid, d.context, d.send_count, d.request_count);
+
+	if (d.context == DRM_KERNEL_CONTEXT || d.context >= dev->queue_slots) {
+		DRM_ERROR("Process %d using context %d\n",
+			  current->pid, d.context);
+		return -EINVAL;
+	}
+	if (d.send_count < 0 || d.send_count > dma->buf_count) {
+		DRM_ERROR("Process %d trying to send %d buffers (of %d max)\n",
+			  current->pid, d.send_count, dma->buf_count);
+		return -EINVAL;
+	}
+	if (d.request_count < 0 || d.request_count > dma->buf_count) {
+		DRM_ERROR("Process %d trying to get %d buffers (of %d max)\n",
+			  current->pid, d.request_count, dma->buf_count);
+		return -EINVAL;
+	}
+
+	if (d.send_count) {
+		if (d.flags & _DRM_DMA_PRIORITY)
+			retcode = gamma_dma_priority(dev, &d);
+		else 
+			retcode = gamma_dma_send_buffers(dev, &d);
+	}
+
+	d.granted_count = 0;
+
+	if (!retcode && d.request_count) {
+		retcode = drm_dma_get_buffers(dev, &d);
+	}
+
+	DRM_DEBUG("%d returning, granted = %d\n",
+		  current->pid, d.granted_count);
+	if (copy_to_user((drm_dma_t *)arg, &d, sizeof(d)))
+		return -EFAULT;
+
+	return retcode;
+}
+
+int gamma_irq_install(drm_device_t *dev, int irq)
+{
+	int retcode;
+
+	if (!irq)     return -EINVAL;
+	
+	down(&dev->struct_sem);
+	if (dev->irq) {
+		up(&dev->struct_sem);
+		return -EBUSY;
+	}
+	dev->irq = irq;
+	up(&dev->struct_sem);
+	
+	DRM_DEBUG("%d\n", irq);
+
+	dev->context_flag     = 0;
+	dev->interrupt_flag   = 0;
+	dev->dma_flag	      = 0;
+	
+	dev->dma->next_buffer = NULL;
+	dev->dma->next_queue  = NULL;
+	dev->dma->this_buffer = NULL;
+
+	INIT_LIST_HEAD(&dev->tq.list);
+	dev->tq.sync	      = 0;
+	dev->tq.routine	      = gamma_dma_schedule_tq_wrapper;
+	dev->tq.data	      = dev;
+
+
+				/* Before installing handler */
+	GAMMA_WRITE(GAMMA_GCOMMANDMODE, 0);
+	GAMMA_WRITE(GAMMA_GDMACONTROL, 0);
+	
+				/* Install handler */
+	if ((retcode = request_irq(dev->irq,
+				   gamma_dma_service,
+				   0,
+				   dev->devname,
+				   dev))) {
+		down(&dev->struct_sem);
+		dev->irq = 0;
+		up(&dev->struct_sem);
+		return retcode;
+	}
+
+				/* After installing handler */
+	GAMMA_WRITE(GAMMA_GINTENABLE,	    0x2001);
+	GAMMA_WRITE(GAMMA_COMMANDINTENABLE, 0x0008);
+	GAMMA_WRITE(GAMMA_GDELAYTIMER,	   0x39090);
+	
+	return 0;
+}
+
+int gamma_irq_uninstall(drm_device_t *dev)
+{
+	int irq;
+
+	down(&dev->struct_sem);
+	irq	 = dev->irq;
+	dev->irq = 0;
+	up(&dev->struct_sem);
+	
+	if (!irq) return -EINVAL;
+	
+	DRM_DEBUG("%d\n", irq);
+	
+	GAMMA_WRITE(GAMMA_GDELAYTIMER,	    0);
+	GAMMA_WRITE(GAMMA_COMMANDINTENABLE, 0);
+	GAMMA_WRITE(GAMMA_GINTENABLE,	    0);
+	free_irq(irq, dev);
+
+	return 0;
+}
+
+
+int gamma_control(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_control_t	ctl;
+	int		retcode;
+	
+	if (copy_from_user(&ctl, (drm_control_t *)arg, sizeof(ctl)))
+		return -EFAULT;
+	
+	switch (ctl.func) {
+	case DRM_INST_HANDLER:
+		if ((retcode = gamma_irq_install(dev, ctl.irq)))
+			return retcode;
+		break;
+	case DRM_UNINST_HANDLER:
+		if ((retcode = gamma_irq_uninstall(dev)))
+			return retcode;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+int gamma_lock(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	DECLARE_WAITQUEUE(entry, current);
+	int		  ret	= 0;
+	drm_lock_t	  lock;
+	drm_queue_t	  *q;
+#if DRM_DMA_HISTOGRAM
+	cycles_t	  start;
+
+	dev->lck_start = start = get_cycles();
+#endif
+
+	if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)))
+		return -EFAULT;
+
+	if (lock.context == DRM_KERNEL_CONTEXT) {
+		DRM_ERROR("Process %d using kernel context %d\n",
+			  current->pid, lock.context);
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
+		  lock.context, current->pid, dev->lock.hw_lock->lock,
+		  lock.flags);
+
+	if (lock.context < 0 || lock.context >= dev->queue_count)
+		return -EINVAL;
+	q = dev->queuelist[lock.context];
+	
+	ret = drm_flush_block_and_flush(dev, lock.context, lock.flags);
+
+	if (!ret) {
+		if (_DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)
+		    != lock.context) {
+			long j = jiffies - dev->lock.lock_time;
+
+			if (j > 0 && j <= DRM_LOCK_SLICE) {
+				/* Can't take lock if we just had it and
+				   there is contention. */
+				current->state = TASK_INTERRUPTIBLE;
+				schedule_timeout(j);
+			}
+		}
+		add_wait_queue(&dev->lock.lock_queue, &entry);
+		for (;;) {
+			current->state = TASK_INTERRUPTIBLE;
+			if (!dev->lock.hw_lock) {
+				/* Device has been unregistered */
+				ret = -EINTR;
+				break;
+			}
+			if (drm_lock_take(&dev->lock.hw_lock->lock,
+					  lock.context)) {
+				dev->lock.pid	    = current->pid;
+				dev->lock.lock_time = jiffies;
+				atomic_inc(&dev->total_locks);
+				atomic_inc(&q->total_locks);
+				break;	/* Got lock */
+			}
+			
+				/* Contention */
+			atomic_inc(&dev->total_sleeps);
+			schedule();
+			if (signal_pending(current)) {
+				ret = -ERESTARTSYS;
+				break;
+			}
+		}
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&dev->lock.lock_queue, &entry);
+	}
+
+	drm_flush_unblock(dev, lock.context, lock.flags); /* cleanup phase */
+	
+	if (!ret) {
+		sigemptyset(&dev->sigmask);
+		sigaddset(&dev->sigmask, SIGSTOP);
+		sigaddset(&dev->sigmask, SIGTSTP);
+		sigaddset(&dev->sigmask, SIGTTIN);
+		sigaddset(&dev->sigmask, SIGTTOU);
+		dev->sigdata.context = lock.context;
+		dev->sigdata.lock    = dev->lock.hw_lock;
+		block_all_signals(drm_notifier, &dev->sigdata, &dev->sigmask);
+
+		if (lock.flags & _DRM_LOCK_READY)
+			gamma_dma_ready(dev);
+		if (lock.flags & _DRM_LOCK_QUIESCENT) {
+			if (gamma_found() == 1) {
+				gamma_dma_quiescent_single(dev);
+			} else {
+				gamma_dma_quiescent_dual(dev);
+			}
+		}
+	}
+	DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
+
+#if DRM_DMA_HISTOGRAM
+	atomic_inc(&dev->histo.lacq[drm_histogram_slot(get_cycles() - start)]);
+#endif
+	
+	return ret;
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/gamma_drv.c linux/drivers/char/drm-4.0/gamma_drv.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/gamma_drv.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/gamma_drv.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,572 @@
+/* gamma.c -- 3dlabs GMX 2000 driver -*- linux-c -*-
+ * Created: Mon Jan  4 08:58:31 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#include <linux/config.h>
+#include "drmP.h"
+#include "gamma_drv.h"
+
+#ifndef PCI_DEVICE_ID_3DLABS_GAMMA
+#define PCI_DEVICE_ID_3DLABS_GAMMA 0x0008
+#endif
+#ifndef PCI_DEVICE_ID_3DLABS_MX
+#define PCI_DEVICE_ID_3DLABS_MX 0x0006
+#endif
+
+#define GAMMA_NAME	 "gamma"
+#define GAMMA_DESC	 "3dlabs GMX 2000"
+#define GAMMA_DATE	 "20000910"
+#define GAMMA_MAJOR	 1
+#define GAMMA_MINOR	 0
+#define GAMMA_PATCHLEVEL 0
+
+static drm_device_t	      gamma_device;
+
+static struct file_operations gamma_fops = {
+#if LINUX_VERSION_CODE >= 0x020400
+				/* This started being used during 2.4.0-test */
+	owner:   THIS_MODULE,
+#endif
+	open:	 gamma_open,
+	flush:	 drm_flush,
+	release: gamma_release,
+	ioctl:	 gamma_ioctl,
+	mmap:	 drm_mmap,
+	read:	 drm_read,
+	fasync:	 drm_fasync,
+	poll:	 drm_poll,
+};
+
+static struct miscdevice      gamma_misc = {
+	minor: MISC_DYNAMIC_MINOR,
+	name:  GAMMA_NAME,
+	fops:  &gamma_fops,
+};
+
+static drm_ioctl_desc_t	      gamma_ioctls[] = {
+	[DRM_IOCTL_NR(DRM_IOCTL_VERSION)]    = { gamma_version,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)] = { drm_getunique,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)]  = { drm_getmagic,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)]  = { drm_irq_busid,	  0, 1 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)] = { drm_setunique,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_BLOCK)]	     = { drm_block,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)]    = { drm_unblock,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_CONTROL)]    = { gamma_control,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)] = { drm_authmagic,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)]    = { drm_addmap,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)]   = { drm_addbufs,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)]  = { drm_markbufs,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)]  = { drm_infobufs,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MAP_BUFS)]   = { drm_mapbufs,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)]  = { drm_freebufs,	  1, 0 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)]    = { drm_addctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)]     = { drm_rmctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)]    = { drm_modctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)]    = { drm_getctx,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { drm_switchctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)]    = { drm_newctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)]    = { drm_resctx,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)]   = { drm_adddraw,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)]    = { drm_rmdraw,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_DMA)]	     = { gamma_dma,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_LOCK)]	     = { gamma_lock,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)]     = { gamma_unlock,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FINISH)]     = { drm_finish,	  1, 0 },
+};
+#define GAMMA_IOCTL_COUNT DRM_ARRAY_SIZE(gamma_ioctls)
+
+#ifdef MODULE
+static char		      *gamma = NULL;
+#endif
+static int 		      devices = 0;
+
+MODULE_AUTHOR("VA Linux Systems, Inc.");
+MODULE_DESCRIPTION("3dlabs GMX 2000");
+MODULE_LICENSE("GPL and additional rights");
+MODULE_PARM(gamma, "s");
+MODULE_PARM(devices, "i");
+MODULE_PARM_DESC(devices,
+		 "devices=x, where x is the number of MX chips on card\n");
+#ifndef MODULE
+/* gamma_options is called by the kernel to parse command-line options
+ * passed via the boot-loader (e.g., LILO).  It calls the insmod option
+ * routine, drm_parse_options.
+ */
+
+
+static int __init gamma_options(char *str)
+{
+	drm_parse_options(str);
+	return 1;
+}
+
+__setup("gamma=", gamma_options);
+#endif
+
+static int gamma_setup(drm_device_t *dev)
+{
+	int i;
+
+	atomic_set(&dev->ioctl_count, 0);
+	atomic_set(&dev->vma_count, 0);
+	dev->buf_use	  = 0;
+	atomic_set(&dev->buf_alloc, 0);
+
+	drm_dma_setup(dev);
+
+	atomic_set(&dev->total_open, 0);
+	atomic_set(&dev->total_close, 0);
+	atomic_set(&dev->total_ioctl, 0);
+	atomic_set(&dev->total_irq, 0);
+	atomic_set(&dev->total_ctx, 0);
+	atomic_set(&dev->total_locks, 0);
+	atomic_set(&dev->total_unlocks, 0);
+	atomic_set(&dev->total_contends, 0);
+	atomic_set(&dev->total_sleeps, 0);
+
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		dev->magiclist[i].head = NULL;
+		dev->magiclist[i].tail = NULL;
+	}
+	dev->maplist	    = NULL;
+	dev->map_count	    = 0;
+	dev->vmalist	    = NULL;
+	dev->lock.hw_lock   = NULL;
+	init_waitqueue_head(&dev->lock.lock_queue);
+	dev->queue_count    = 0;
+	dev->queue_reserved = 0;
+	dev->queue_slots    = 0;
+	dev->queuelist	    = NULL;
+	dev->irq	    = 0;
+	dev->context_flag   = 0;
+	dev->interrupt_flag = 0;
+	dev->dma_flag	    = 0;
+	dev->last_context   = 0;
+	dev->last_switch    = 0;
+	dev->last_checked   = 0;
+	init_timer(&dev->timer);
+	init_waitqueue_head(&dev->context_wait);
+#if DRM_DMA_HISTO
+	memset(&dev->histo, 0, sizeof(dev->histo));
+#endif
+	dev->ctx_start	    = 0;
+	dev->lck_start	    = 0;
+
+	dev->buf_rp	  = dev->buf;
+	dev->buf_wp	  = dev->buf;
+	dev->buf_end	  = dev->buf + DRM_BSZ;
+	dev->buf_async	  = NULL;
+	init_waitqueue_head(&dev->buf_readers);
+	init_waitqueue_head(&dev->buf_writers);
+
+	DRM_DEBUG("\n");
+
+	/* The kernel's context could be created here, but is now created
+	   in drm_dma_enqueue.	This is more resource-efficient for
+	   hardware that does not do DMA, but may mean that
+	   drm_select_queue fails between the time the interrupt is
+	   initialized and the time the queues are initialized. */
+
+	return 0;
+}
+
+
+static int gamma_takedown(drm_device_t *dev)
+{
+	int		  i;
+	drm_magic_entry_t *pt, *next;
+	drm_map_t	  *map;
+	drm_vma_entry_t	  *vma, *vma_next;
+
+	DRM_DEBUG("\n");
+
+	if (dev->irq) gamma_irq_uninstall(dev);
+
+	down(&dev->struct_sem);
+	del_timer(&dev->timer);
+
+	if (dev->devname) {
+		drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER);
+		dev->devname = NULL;
+	}
+
+	if (dev->unique) {
+		drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER);
+		dev->unique = NULL;
+		dev->unique_len = 0;
+	}
+				/* Clear pid list */
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		for (pt = dev->magiclist[i].head; pt; pt = next) {
+			next = pt->next;
+			drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
+		}
+		dev->magiclist[i].head = dev->magiclist[i].tail = NULL;
+	}
+
+				/* Clear vma list (only built for debugging) */
+	if (dev->vmalist) {
+		for (vma = dev->vmalist; vma; vma = vma_next) {
+			vma_next = vma->next;
+			drm_free(vma, sizeof(*vma), DRM_MEM_VMAS);
+		}
+		dev->vmalist = NULL;
+	}
+
+				/* Clear map area and mtrr information */
+	if (dev->maplist) {
+		for (i = 0; i < dev->map_count; i++) {
+			map = dev->maplist[i];
+			switch (map->type) {
+			case _DRM_REGISTERS:
+			case _DRM_FRAME_BUFFER:
+#ifdef CONFIG_MTRR
+				if (map->mtrr >= 0) {
+					int retcode;
+					retcode = mtrr_del(map->mtrr,
+							   map->offset,
+							   map->size);
+					DRM_DEBUG("mtrr_del = %d\n", retcode);
+				}
+#endif
+				drm_ioremapfree(map->handle, map->size);
+				break;
+			case _DRM_SHM:
+				drm_free_pages((unsigned long)map->handle,
+					       drm_order(map->size)
+					       - PAGE_SHIFT,
+					       DRM_MEM_SAREA);
+				break;
+			case _DRM_AGP:
+				/* Do nothing here, because this is all
+                                   handled in the AGP/GART driver. */
+				break;
+			}
+			drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+		}
+		drm_free(dev->maplist,
+			 dev->map_count * sizeof(*dev->maplist),
+			 DRM_MEM_MAPS);
+		dev->maplist   = NULL;
+		dev->map_count = 0;
+	}
+
+	if (dev->queuelist) {
+		for (i = 0; i < dev->queue_count; i++) {
+			drm_waitlist_destroy(&dev->queuelist[i]->waitlist);
+			if (dev->queuelist[i]) {
+				drm_free(dev->queuelist[i],
+					 sizeof(*dev->queuelist[0]),
+					 DRM_MEM_QUEUES);
+				dev->queuelist[i] = NULL;
+			}
+		}
+		drm_free(dev->queuelist,
+			 dev->queue_slots * sizeof(*dev->queuelist),
+			 DRM_MEM_QUEUES);
+		dev->queuelist	 = NULL;
+	}
+
+	drm_dma_takedown(dev);
+
+	dev->queue_count     = 0;
+	if (dev->lock.hw_lock) {
+		dev->lock.hw_lock    = NULL; /* SHM removed */
+		dev->lock.pid	     = 0;
+		wake_up_interruptible(&dev->lock.lock_queue);
+	}
+	up(&dev->struct_sem);
+
+	return 0;
+}
+
+int gamma_found(void)
+{
+	return devices;
+}
+
+int gamma_find_devices(void)
+{
+	struct pci_dev *d = NULL, *one = NULL, *two = NULL;
+
+	d = pci_find_device(PCI_VENDOR_ID_3DLABS,PCI_DEVICE_ID_3DLABS_GAMMA,d);
+	if (!d) return 0;
+
+	one = pci_find_device(PCI_VENDOR_ID_3DLABS,PCI_DEVICE_ID_3DLABS_MX,d);
+	if (!one) return 0;
+
+	/* Make sure it's on the same card, if not - no MX's found */
+	if (PCI_SLOT(d->devfn) != PCI_SLOT(one->devfn)) return 0;
+
+	two = pci_find_device(PCI_VENDOR_ID_3DLABS,PCI_DEVICE_ID_3DLABS_MX,one);
+	if (!two) return 1;
+
+	/* Make sure it's on the same card, if not - only 1 MX found */
+	if (PCI_SLOT(d->devfn) != PCI_SLOT(two->devfn)) return 1;
+
+	/* Two MX's found - we don't currently support more than 2 */
+	return 2;
+}
+
+/* gamma_init is called via init_module at module load time, or via
+ * linux/init/main.c (this is not currently supported). */
+
+static int __init gamma_init(void)
+{
+	int		      retcode;
+	drm_device_t	      *dev = &gamma_device;
+
+	DRM_DEBUG("\n");
+
+	memset((void *)dev, 0, sizeof(*dev));
+	dev->count_lock	  = SPIN_LOCK_UNLOCKED;
+	sema_init(&dev->struct_sem, 1);
+
+#ifdef MODULE
+	drm_parse_options(gamma);
+#endif
+	devices = gamma_find_devices();
+	if (devices == 0) return -1;
+
+	if ((retcode = misc_register(&gamma_misc))) {
+		DRM_ERROR("Cannot register \"%s\"\n", GAMMA_NAME);
+		return retcode;
+	}
+	dev->device = MKDEV(MISC_MAJOR, gamma_misc.minor);
+	dev->name   = GAMMA_NAME;
+
+	drm_mem_init();
+	drm_proc_init(dev);
+
+	DRM_INFO("Initialized %s %d.%d.%d %s on minor %d with %d MX devices\n",
+		 GAMMA_NAME,
+		 GAMMA_MAJOR,
+		 GAMMA_MINOR,
+		 GAMMA_PATCHLEVEL,
+		 GAMMA_DATE,
+		 gamma_misc.minor,
+		 devices);
+
+	return 0;
+}
+
+/* gamma_cleanup is called via cleanup_module at module unload time. */
+
+static void __exit gamma_cleanup(void)
+{
+	drm_device_t	      *dev = &gamma_device;
+
+	DRM_DEBUG("\n");
+
+	drm_proc_cleanup();
+	if (misc_deregister(&gamma_misc)) {
+		DRM_ERROR("Cannot unload module\n");
+	} else {
+		DRM_INFO("Module unloaded\n");
+	}
+	gamma_takedown(dev);
+}
+
+module_init(gamma_init);
+module_exit(gamma_cleanup);
+
+
+int gamma_version(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_version_t version;
+	int	      len;
+
+	if (copy_from_user(&version,
+			   (drm_version_t *)arg,
+			   sizeof(version)))
+		return -EFAULT;
+
+#define DRM_COPY(name,value)				     \
+	len = strlen(value);				     \
+	if (len > name##_len) len = name##_len;		     \
+	name##_len = strlen(value);			     \
+	if (len && name) {				     \
+		if (copy_to_user(name, value, len))	     \
+			return -EFAULT;			     \
+	}
+
+	version.version_major	   = GAMMA_MAJOR;
+	version.version_minor	   = GAMMA_MINOR;
+	version.version_patchlevel = GAMMA_PATCHLEVEL;
+
+	DRM_COPY(version.name, GAMMA_NAME);
+	DRM_COPY(version.date, GAMMA_DATE);
+	DRM_COPY(version.desc, GAMMA_DESC);
+
+	if (copy_to_user((drm_version_t *)arg,
+			 &version,
+			 sizeof(version)))
+		return -EFAULT;
+	return 0;
+}
+
+int gamma_open(struct inode *inode, struct file *filp)
+{
+	drm_device_t  *dev    = &gamma_device;
+	int	      retcode = 0;
+
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	if (!(retcode = drm_open_helper(inode, filp, dev))) {
+#if LINUX_VERSION_CODE < 0x020333
+		MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+		atomic_inc(&dev->total_open);
+		spin_lock(&dev->count_lock);
+		if (!dev->open_count++) {
+			spin_unlock(&dev->count_lock);
+			return gamma_setup(dev);
+		}
+		spin_unlock(&dev->count_lock);
+	}
+	return retcode;
+}
+
+int gamma_release(struct inode *inode, struct file *filp)
+{
+	drm_file_t    *priv   = filp->private_data;
+	drm_device_t  *dev;
+	int	      retcode = 0;
+
+	lock_kernel();
+	dev = priv->dev;
+
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	if (!(retcode = drm_release(inode, filp))) {
+#if LINUX_VERSION_CODE < 0x020333
+		MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+		atomic_inc(&dev->total_close);
+		spin_lock(&dev->count_lock);
+		if (!--dev->open_count) {
+			if (atomic_read(&dev->ioctl_count) || dev->blocked) {
+				DRM_ERROR("Device busy: %d %d\n",
+					  atomic_read(&dev->ioctl_count),
+					  dev->blocked);
+				spin_unlock(&dev->count_lock);
+				unlock_kernel();
+				return -EBUSY;
+			}
+			spin_unlock(&dev->count_lock);
+			unlock_kernel();
+			return gamma_takedown(dev);
+		}
+		spin_unlock(&dev->count_lock);
+	}
+	unlock_kernel();
+	return retcode;
+}
+
+/* drm_ioctl is called whenever a process performs an ioctl on /dev/drm. */
+
+int gamma_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	int		 nr	 = DRM_IOCTL_NR(cmd);
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	int		 retcode = 0;
+	drm_ioctl_desc_t *ioctl;
+	drm_ioctl_t	 *func;
+
+	atomic_inc(&dev->ioctl_count);
+	atomic_inc(&dev->total_ioctl);
+	++priv->ioctl_count;
+
+	DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n",
+		  current->pid, cmd, nr, dev->device, priv->authenticated);
+
+	if (nr >= GAMMA_IOCTL_COUNT) {
+		retcode = -EINVAL;
+	} else {
+		ioctl	  = &gamma_ioctls[nr];
+		func	  = ioctl->func;
+
+		if (!func) {
+			DRM_DEBUG("no function\n");
+			retcode = -EINVAL;
+		} else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN))
+			    || (ioctl->auth_needed && !priv->authenticated)) {
+			retcode = -EACCES;
+		} else {
+			retcode = (func)(inode, filp, cmd, arg);
+		}
+	}
+
+	atomic_dec(&dev->ioctl_count);
+	return retcode;
+}
+
+
+int gamma_unlock(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	drm_lock_t	  lock;
+
+	if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)))
+		return -EFAULT;
+
+	if (lock.context == DRM_KERNEL_CONTEXT) {
+		DRM_ERROR("Process %d using kernel context %d\n",
+			  current->pid, lock.context);
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("%d frees lock (%d holds)\n",
+		  lock.context,
+		  _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+	atomic_inc(&dev->total_unlocks);
+	if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock))
+		atomic_inc(&dev->total_contends);
+	drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT);
+	gamma_dma_schedule(dev, 1);
+	if (!dev->context_flag) {
+		if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				  DRM_KERNEL_CONTEXT)) {
+			DRM_ERROR("\n");
+		}
+	}
+#if DRM_DMA_HISTOGRAM
+	atomic_inc(&dev->histo.lhld[drm_histogram_slot(get_cycles()
+						       - dev->lck_start)]);
+#endif
+
+	unblock_all_signals();
+	return 0;
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/gamma_drv.h linux/drivers/char/drm-4.0/gamma_drv.h
--- linux-2.4.18-pre2/drivers/char/drm-4.0/gamma_drv.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/gamma_drv.h	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,58 @@
+/* gamma_drv.h -- Private header for 3dlabs GMX 2000 driver -*- linux-c -*-
+ * Created: Mon Jan  4 10:05:05 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ * 
+ */
+
+#ifndef _GAMMA_DRV_H_
+#define _GAMMA_DRV_H_
+
+				/* gamma_drv.c */
+extern int  gamma_version(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  gamma_open(struct inode *inode, struct file *filp);
+extern int  gamma_release(struct inode *inode, struct file *filp);
+extern int  gamma_ioctl(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  gamma_lock(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  gamma_unlock(struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg);
+
+				/* gamma_dma.c */
+extern int  gamma_dma_schedule(drm_device_t *dev, int locked);
+extern int  gamma_dma(struct inode *inode, struct file *filp,
+		      unsigned int cmd, unsigned long arg);
+extern int  gamma_irq_install(drm_device_t *dev, int irq);
+extern int  gamma_irq_uninstall(drm_device_t *dev);
+extern int  gamma_control(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  gamma_find_devices(void);
+extern int  gamma_found(void);
+
+#endif
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/i810_bufs.c linux/drivers/char/drm-4.0/i810_bufs.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/i810_bufs.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/i810_bufs.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,339 @@
+/* i810_bufs.c -- IOCTLs to manage buffers -*- linux-c -*-
+ * Created: Thu Jan 6 01:47:26 2000 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
+ *	    Jeff Hartmann <jhartmann@valinux.com>
+ * 
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "i810_drv.h"
+#include "linux/un.h"
+
+int i810_addbufs_agp(struct inode *inode, struct file *filp, unsigned int cmd,
+		    unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_device_dma_t *dma = dev->dma;
+	drm_buf_desc_t request;
+	drm_buf_entry_t *entry;
+	drm_buf_t *buf;
+	unsigned long offset;
+	unsigned long agp_offset;
+	int count;
+	int order;
+	int size;
+	int alignment;
+	int page_order;
+	int total;
+	int byte_count;
+	int i;
+
+	if (!dma) return -EINVAL;
+
+	if (copy_from_user(&request,
+			   (drm_buf_desc_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	count = request.count;
+	order = drm_order(request.size);
+	size	= 1 << order;
+	agp_offset = request.agp_start;
+	alignment  = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size) :size;
+	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
+	total = PAGE_SIZE << page_order;
+	byte_count = 0;
+   
+	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+	if (dev->queue_count) return -EBUSY; /* Not while in use */
+	spin_lock(&dev->count_lock);
+	if (dev->buf_use) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	atomic_inc(&dev->buf_alloc);
+	spin_unlock(&dev->count_lock);
+   
+	down(&dev->struct_sem);
+	entry = &dma->bufs[order];
+	if (entry->buf_count) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM; /* May only call once for each order */
+	}
+
+	if(count < 0 || count > 4096)
+	{
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -EINVAL;
+	}
+	   
+	entry->buflist = drm_alloc(count * sizeof(*entry->buflist),
+				   DRM_MEM_BUFS);
+	if (!entry->buflist) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM;
+	}
+	memset(entry->buflist, 0, count * sizeof(*entry->buflist));
+   
+	entry->buf_size   = size;
+	entry->page_order = page_order;
+	offset = 0;
+   
+	while(entry->buf_count < count) {
+		buf = &entry->buflist[entry->buf_count];
+		buf->idx = dma->buf_count + entry->buf_count;
+		buf->total = alignment;
+		buf->order = order;
+		buf->used = 0;
+		buf->offset = offset;
+		buf->bus_address = dev->agp->base + agp_offset + offset;
+		buf->address = (void *)(agp_offset + offset + dev->agp->base);
+		buf->next = NULL;
+		buf->waiting = 0;
+		buf->pending = 0;
+		init_waitqueue_head(&buf->dma_wait);
+		buf->pid = 0;
+
+		buf->dev_private = drm_alloc(sizeof(drm_i810_buf_priv_t), 
+					     DRM_MEM_BUFS);
+		buf->dev_priv_size = sizeof(drm_i810_buf_priv_t);
+	   	memset(buf->dev_private, 0, sizeof(drm_i810_buf_priv_t));
+
+#if DRM_DMA_HISTOGRAM
+		buf->time_queued = 0;
+		buf->time_dispatched = 0;
+		buf->time_completed = 0;
+		buf->time_freed = 0;
+#endif
+		offset = offset + alignment;
+		entry->buf_count++;
+		byte_count += PAGE_SIZE << page_order;
+      
+		DRM_DEBUG("buffer %d @ %p\n",
+			  entry->buf_count, buf->address);
+	}
+   
+	dma->buflist = drm_realloc(dma->buflist,
+				   dma->buf_count * sizeof(*dma->buflist),
+				   (dma->buf_count + entry->buf_count)
+				   * sizeof(*dma->buflist),
+				   DRM_MEM_BUFS);
+	for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++)
+		dma->buflist[i] = &entry->buflist[i - dma->buf_count];
+   
+	dma->buf_count  += entry->buf_count;
+	dma->byte_count += byte_count;
+	drm_freelist_create(&entry->freelist, entry->buf_count);
+	for (i = 0; i < entry->buf_count; i++) {
+		drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]);
+	}
+   
+	up(&dev->struct_sem);
+   
+	request.count = entry->buf_count;
+	request.size  = size;
+   
+	if (copy_to_user((drm_buf_desc_t *)arg,
+			 &request,
+			 sizeof(request)))
+		return -EFAULT;
+   
+	atomic_dec(&dev->buf_alloc);
+	dma->flags = _DRM_DMA_USE_AGP;
+	return 0;
+}
+
+int i810_addbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_buf_desc_t	 request;
+
+	if (copy_from_user(&request,
+			   (drm_buf_desc_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	if(request.flags & _DRM_AGP_BUFFER)
+		return i810_addbufs_agp(inode, filp, cmd, arg);
+	else
+		return -EINVAL;
+}
+
+int i810_infobufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_info_t	 request;
+	int		 i;
+	int		 count;
+
+	if (!dma) return -EINVAL;
+
+	spin_lock(&dev->count_lock);
+	if (atomic_read(&dev->buf_alloc)) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	++dev->buf_use;		/* Can't allocate more after this call */
+	spin_unlock(&dev->count_lock);
+
+	if (copy_from_user(&request,
+			   (drm_buf_info_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) {
+		if (dma->bufs[i].buf_count) ++count;
+	}
+	
+	DRM_DEBUG("count = %d\n", count);
+	
+	if (request.count >= count) {
+		for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) {
+			if (dma->bufs[i].buf_count) {
+				if (copy_to_user(&request.list[count].count,
+						 &dma->bufs[i].buf_count,
+						 sizeof(dma->bufs[0]
+							.buf_count)) ||
+				    copy_to_user(&request.list[count].size,
+						 &dma->bufs[i].buf_size,
+						 sizeof(dma->bufs[0].buf_size)) ||
+				    copy_to_user(&request.list[count].low_mark,
+						 &dma->bufs[i]
+						 .freelist.low_mark,
+						 sizeof(dma->bufs[0]
+							.freelist.low_mark)) ||
+				    copy_to_user(&request.list[count]
+						 .high_mark,
+						 &dma->bufs[i]
+						 .freelist.high_mark,
+						 sizeof(dma->bufs[0]
+							.freelist.high_mark)))
+					return -EFAULT;
+
+				DRM_DEBUG("%d %d %d %d %d\n",
+					  i,
+					  dma->bufs[i].buf_count,
+					  dma->bufs[i].buf_size,
+					  dma->bufs[i].freelist.low_mark,
+					  dma->bufs[i].freelist.high_mark);
+				++count;
+			}
+		}
+	}
+	request.count = count;
+
+	if (copy_to_user((drm_buf_info_t *)arg,
+			 &request,
+			 sizeof(request)))
+		return -EFAULT;
+	
+	return 0;
+}
+
+int i810_markbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_desc_t	 request;
+	int		 order;
+	drm_buf_entry_t	 *entry;
+
+	if (!dma) return -EINVAL;
+
+	if (copy_from_user(&request,
+			   (drm_buf_desc_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	DRM_DEBUG("%d, %d, %d\n",
+		  request.size, request.low_mark, request.high_mark);
+	order = drm_order(request.size);
+	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+	entry = &dma->bufs[order];
+
+	if (request.low_mark < 0 || request.low_mark > entry->buf_count)
+		return -EINVAL;
+	if (request.high_mark < 0 || request.high_mark > entry->buf_count)
+		return -EINVAL;
+
+	entry->freelist.low_mark  = request.low_mark;
+	entry->freelist.high_mark = request.high_mark;
+	
+	return 0;
+}
+
+int i810_freebufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_free_t	 request;
+	int		 i;
+	int		 idx;
+	drm_buf_t	 *buf;
+
+	if (!dma) return -EINVAL;
+
+	if (copy_from_user(&request,
+			   (drm_buf_free_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	DRM_DEBUG("%d\n", request.count);
+	for (i = 0; i < request.count; i++) {
+		if (copy_from_user(&idx,
+				   &request.list[i],
+				   sizeof(idx)))
+			return -EFAULT;
+		if (idx < 0 || idx >= dma->buf_count) {
+			DRM_ERROR("Index %d (of %d max)\n",
+				  idx, dma->buf_count - 1);
+			return -EINVAL;
+		}
+		buf = dma->buflist[idx];
+		if (buf->pid != current->pid) {
+			DRM_ERROR("Process %d freeing buffer owned by %d\n",
+				  current->pid, buf->pid);
+			return -EINVAL;
+		}
+		drm_free_buffer(dev, buf);
+	}
+	
+	return 0;
+}
+
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/i810_context.c linux/drivers/char/drm-4.0/i810_context.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/i810_context.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/i810_context.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,212 @@
+/* i810_context.c -- IOCTLs for i810 contexts -*- linux-c -*-
+ * Created: Mon Dec 13 09:51:35 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
+ *	    Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "i810_drv.h"
+
+static int i810_alloc_queue(drm_device_t *dev)
+{
+   	int temp = drm_ctxbitmap_next(dev);
+   	DRM_DEBUG("i810_alloc_queue: %d\n", temp);
+	return temp;
+}
+
+int i810_context_switch(drm_device_t *dev, int old, int new)
+{
+        char        buf[64];
+
+        atomic_inc(&dev->total_ctx);
+
+        if (test_and_set_bit(0, &dev->context_flag)) {
+                DRM_ERROR("Reentering -- FIXME\n");
+                return -EBUSY;
+        }
+
+#if DRM_DMA_HISTOGRAM
+        dev->ctx_start = get_cycles();
+#endif
+        
+        DRM_DEBUG("Context switch from %d to %d\n", old, new);
+
+        if (new == dev->last_context) {
+                clear_bit(0, &dev->context_flag);
+                return 0;
+        }
+        
+        if (drm_flags & DRM_FLAG_NOCTX) {
+                i810_context_switch_complete(dev, new);
+        } else {
+                sprintf(buf, "C %d %d\n", old, new);
+                drm_write_string(dev, buf);
+        }
+        
+        return 0;
+}
+
+int i810_context_switch_complete(drm_device_t *dev, int new)
+{
+        dev->last_context = new;  /* PRE/POST: This is the _only_ writer. */
+        dev->last_switch  = jiffies;
+        
+        if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+                DRM_ERROR("Lock isn't held after context switch\n");
+        }
+
+				/* If a context switch is ever initiated
+                                   when the kernel holds the lock, release
+                                   that lock here. */
+#if DRM_DMA_HISTOGRAM
+        atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles()
+                                                      - dev->ctx_start)]);
+                   
+#endif
+        clear_bit(0, &dev->context_flag);
+        wake_up(&dev->context_wait);
+        
+        return 0;
+}
+
+int i810_resctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_ctx_res_t	res;
+	drm_ctx_t	ctx;
+	int		i;
+
+	DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS);
+	if (copy_from_user(&res, (drm_ctx_res_t *)arg, sizeof(res)))
+		return -EFAULT;
+	if (res.count >= DRM_RESERVED_CONTEXTS) {
+		memset(&ctx, 0, sizeof(ctx));
+		for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
+			ctx.handle = i;
+			if (copy_to_user(&res.contexts[i],
+					 &i,
+					 sizeof(i)))
+				return -EFAULT;
+		}
+	}
+	res.count = DRM_RESERVED_CONTEXTS;
+	if (copy_to_user((drm_ctx_res_t *)arg, &res, sizeof(res)))
+		return -EFAULT;
+	return 0;
+}
+
+int i810_addctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	if ((ctx.handle = i810_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) {
+				/* Skip kernel's context and get a new one. */
+		ctx.handle = i810_alloc_queue(dev);
+	}
+        if (ctx.handle == -1) {
+		DRM_DEBUG("Not enough free contexts.\n");
+				/* Should this return -EBUSY instead? */
+		return -ENOMEM;
+	}
+	DRM_DEBUG("%d\n", ctx.handle);
+	if (copy_to_user((drm_ctx_t *)arg, &ctx, sizeof(ctx)))
+		return -EFAULT;
+	return 0;
+}
+
+int i810_modctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	unsigned long arg)
+{
+   	/* This does nothing for the i810 */
+	return 0;
+}
+
+int i810_getctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	unsigned long arg)
+{
+	drm_ctx_t ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx)))
+		return -EFAULT;
+	/* This is 0, because we don't hanlde any context flags */
+	ctx.flags = 0;
+	if (copy_to_user((drm_ctx_t*)arg, &ctx, sizeof(ctx)))
+		return -EFAULT;
+	return 0;
+}
+
+int i810_switchctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		   unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	return i810_context_switch(dev, dev->last_context, ctx.handle);
+}
+
+int i810_newctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	i810_context_switch_complete(dev, ctx.handle);
+
+	return 0;
+}
+
+int i810_rmctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	      unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+   	if(ctx.handle != DRM_KERNEL_CONTEXT) {
+	   	drm_ctxbitmap_free(dev, ctx.handle);
+	}
+	
+	return 0;
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/i810_dma.c linux/drivers/char/drm-4.0/i810_dma.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/i810_dma.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/i810_dma.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,1437 @@
+/* i810_dma.c -- DMA support for the i810 -*- linux-c -*-
+ * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
+ *	    Jeff Hartmann <jhartmann@valinux.com>
+ *          Keith Whitwell <keithw@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "i810_drv.h"
+#include <linux/interrupt.h>	/* For task queue support */
+
+/* in case we don't have a 2.3.99-pre6 kernel or later: */
+#ifndef VM_DONTCOPY
+#define VM_DONTCOPY 0
+#endif
+
+#define I810_BUF_FREE		2
+#define I810_BUF_CLIENT		1
+#define I810_BUF_HARDWARE      	0
+
+#define I810_BUF_UNMAPPED 0
+#define I810_BUF_MAPPED   1
+
+#define I810_REG(reg)		2
+#define I810_BASE(reg)		((unsigned long) \
+				dev->maplist[I810_REG(reg)]->handle)
+#define I810_ADDR(reg)		(I810_BASE(reg) + reg)
+#define I810_DEREF(reg)		*(__volatile__ int *)I810_ADDR(reg)
+#define I810_READ(reg)		I810_DEREF(reg)
+#define I810_WRITE(reg,val) 	do { I810_DEREF(reg) = val; } while (0)
+#define I810_DEREF16(reg)	*(__volatile__ u16 *)I810_ADDR(reg)
+#define I810_READ16(reg)	I810_DEREF16(reg)
+#define I810_WRITE16(reg,val)	do { I810_DEREF16(reg) = val; } while (0)
+
+#define RING_LOCALS	unsigned int outring, ringmask; volatile char *virt;
+
+#define BEGIN_LP_RING(n) do {				\
+	if (I810_VERBOSE)				\
+		DRM_DEBUG("BEGIN_LP_RING(%d) in %s\n",	\
+			  n, __FUNCTION__);		\
+	if (dev_priv->ring.space < n*4) 		\
+		i810_wait_ring(dev, n*4);		\
+	dev_priv->ring.space -= n*4;			\
+	outring = dev_priv->ring.tail;			\
+	ringmask = dev_priv->ring.tail_mask;		\
+	virt = dev_priv->ring.virtual_start;		\
+} while (0)
+
+#define ADVANCE_LP_RING() do {					\
+	if (I810_VERBOSE) DRM_DEBUG("ADVANCE_LP_RING\n");	\
+	dev_priv->ring.tail = outring;				\
+	I810_WRITE(LP_RING + RING_TAIL, outring);		\
+} while(0)
+
+#define OUT_RING(n) do {						\
+	if (I810_VERBOSE) DRM_DEBUG("   OUT_RING %x\n", (int)(n));	\
+	*(volatile unsigned int *)(virt + outring) = n;			\
+	outring += 4;							\
+	outring &= ringmask;						\
+} while (0);
+
+static inline void i810_print_status_page(drm_device_t *dev)
+{
+   	drm_device_dma_t *dma = dev->dma;
+      	drm_i810_private_t *dev_priv = dev->dev_private;
+	u32 *temp = (u32 *)dev_priv->hw_status_page;
+   	int i;
+
+   	DRM_DEBUG(  "hw_status: Interrupt Status : %x\n", temp[0]);
+   	DRM_DEBUG(  "hw_status: LpRing Head ptr : %x\n", temp[1]);
+   	DRM_DEBUG(  "hw_status: IRing Head ptr : %x\n", temp[2]);
+      	DRM_DEBUG(  "hw_status: Reserved : %x\n", temp[3]);
+   	DRM_DEBUG(  "hw_status: Driver Counter : %d\n", temp[5]);
+   	for(i = 6; i < dma->buf_count + 6; i++) {
+	   	DRM_DEBUG( "buffer status idx : %d used: %d\n", i - 6, temp[i]);
+	}
+}
+
+static drm_buf_t *i810_freelist_get(drm_device_t *dev)
+{
+   	drm_device_dma_t *dma = dev->dma;
+	int		 i;
+   	int 		 used;
+   
+	/* Linear search might not be the best solution */
+
+   	for (i = 0; i < dma->buf_count; i++) {
+	   	drm_buf_t *buf = dma->buflist[ i ];
+	   	drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+		/* In use is already a pointer */
+	   	used = cmpxchg(buf_priv->in_use, I810_BUF_FREE, 
+			       I810_BUF_CLIENT);
+	   	if(used == I810_BUF_FREE) {
+			return buf;
+		}
+	}
+   	return NULL;
+}
+
+/* This should only be called if the buffer is not sent to the hardware
+ * yet, the hardware updates in use for us once its on the ring buffer.
+ */
+
+static int i810_freelist_put(drm_device_t *dev, drm_buf_t *buf)
+{
+   	drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+   	int used;
+   
+   	/* In use is already a pointer */
+   	used = cmpxchg(buf_priv->in_use, I810_BUF_CLIENT, I810_BUF_FREE);
+   	if(used != I810_BUF_CLIENT) {
+	   	DRM_ERROR("Freeing buffer thats not in use : %d\n", buf->idx);
+	   	return -EINVAL;
+	}
+   
+   	return 0;
+}
+
+static struct file_operations i810_buffer_fops = {
+	open:	 i810_open,
+	flush:	 drm_flush,
+	release: i810_release,
+	ioctl:	 i810_ioctl,
+	mmap:	 i810_mmap_buffers,
+	read:	 drm_read,
+	fasync:	 drm_fasync,
+      	poll:	 drm_poll,
+};
+
+int i810_mmap_buffers(struct file *filp, struct vm_area_struct *vma)
+{
+	drm_file_t	    *priv	  = filp->private_data;
+	drm_device_t	    *dev;
+	drm_i810_private_t  *dev_priv;
+	drm_buf_t           *buf;
+	drm_i810_buf_priv_t *buf_priv;
+
+	lock_kernel();
+	dev	 = priv->dev;
+	dev_priv = dev->dev_private;
+	buf      = dev_priv->mmap_buffer;
+	buf_priv = buf->dev_private;
+   
+	vma->vm_flags |= (VM_IO | VM_DONTCOPY);
+	vma->vm_file = filp;
+   
+   	buf_priv->currently_mapped = I810_BUF_MAPPED;
+	unlock_kernel();
+
+	if (remap_page_range(vma->vm_start,
+			     VM_OFFSET(vma),
+			     vma->vm_end - vma->vm_start,
+			     vma->vm_page_prot)) return -EAGAIN;
+	return 0;
+}
+
+static int i810_map_buffer(drm_buf_t *buf, struct file *filp)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+      	drm_i810_private_t *dev_priv = dev->dev_private;
+   	struct file_operations *old_fops;
+	int retcode = 0;
+
+	if(buf_priv->currently_mapped == I810_BUF_MAPPED) return -EINVAL;
+
+	if(VM_DONTCOPY != 0) {
+		down_write(&current->mm->mmap_sem);
+		old_fops = filp->f_op;
+		filp->f_op = &i810_buffer_fops;
+		dev_priv->mmap_buffer = buf;
+		buf_priv->virtual = (void *)do_mmap(filp, 0, buf->total, 
+						    PROT_READ|PROT_WRITE,
+						    MAP_SHARED, 
+						    buf->bus_address);
+		dev_priv->mmap_buffer = NULL;
+   		filp->f_op = old_fops;
+		if ((unsigned long)buf_priv->virtual > -1024UL) {
+			/* Real error */
+			DRM_DEBUG("mmap error\n");
+			retcode = (signed int)buf_priv->virtual;
+			buf_priv->virtual = 0;
+		}
+   		up_write(&current->mm->mmap_sem);
+	} else {
+		buf_priv->virtual = buf_priv->kernel_virtual;
+   		buf_priv->currently_mapped = I810_BUF_MAPPED;
+	}
+	return retcode;
+}
+
+static int i810_unmap_buffer(drm_buf_t *buf)
+{
+	drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+	int retcode = 0;
+
+	if(VM_DONTCOPY != 0) {
+		if(buf_priv->currently_mapped != I810_BUF_MAPPED) 
+			return -EINVAL;
+		down_write(&current->mm->mmap_sem);
+#if LINUX_VERSION_CODE < 0x020399
+        	retcode = do_munmap((unsigned long)buf_priv->virtual, 
+				    (size_t) buf->total);
+#else
+        	retcode = do_munmap(current->mm, 
+				    (unsigned long)buf_priv->virtual, 
+				    (size_t) buf->total);
+#endif
+   		up_write(&current->mm->mmap_sem);
+	}
+   	buf_priv->currently_mapped = I810_BUF_UNMAPPED;
+   	buf_priv->virtual = 0;
+
+	return retcode;
+}
+
+static int i810_dma_get_buffer(drm_device_t *dev, drm_i810_dma_t *d, 
+			       struct file *filp)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_buf_t	  *buf;
+	drm_i810_buf_priv_t *buf_priv;
+	int retcode = 0;
+
+	buf = i810_freelist_get(dev);
+	if (!buf) {
+		retcode = -ENOMEM;
+	   	DRM_DEBUG("retcode=%d\n", retcode);
+		return retcode;
+	}
+   
+	retcode = i810_map_buffer(buf, filp);
+	if(retcode) {
+		i810_freelist_put(dev, buf);
+	   	DRM_DEBUG("mapbuf failed, retcode %d\n", retcode);
+		return retcode;
+	}
+	buf->pid     = priv->pid;
+	buf_priv = buf->dev_private;	
+	d->granted = 1;
+   	d->request_idx = buf->idx;
+   	d->request_size = buf->total;
+   	d->virtual = buf_priv->virtual;
+
+	return retcode;
+}
+
+static unsigned long i810_alloc_page(drm_device_t *dev)
+{
+	unsigned long address;
+   
+	address = __get_free_page(GFP_KERNEL);
+	if(address == 0UL) 
+		return 0;
+	
+	atomic_inc(&virt_to_page(address)->count);
+	set_bit(PG_locked, &virt_to_page(address)->flags);
+   
+	return address;
+}
+
+static void i810_free_page(drm_device_t *dev, unsigned long page)
+{
+	if(page == 0UL) 
+		return;
+	
+	atomic_dec(&virt_to_page(page)->count);
+	clear_bit(PG_locked, &virt_to_page(page)->flags);
+	wake_up(&virt_to_page(page)->wait);
+	free_page(page);
+	return;
+}
+
+static int i810_dma_cleanup(drm_device_t *dev)
+{
+	drm_device_dma_t *dma = dev->dma;
+
+	if(dev->dev_private) {
+		int i;
+	   	drm_i810_private_t *dev_priv = 
+	     		(drm_i810_private_t *) dev->dev_private;
+	   
+	   	if(dev_priv->ring.virtual_start) {
+		   	drm_ioremapfree((void *) dev_priv->ring.virtual_start,
+					dev_priv->ring.Size);
+		}
+	   	if(dev_priv->hw_status_page != 0UL) {
+		   	i810_free_page(dev, dev_priv->hw_status_page);
+		   	/* Need to rewrite hardware status page */
+		   	I810_WRITE(0x02080, 0x1ffff000);
+		}
+	   	drm_free(dev->dev_private, sizeof(drm_i810_private_t), 
+			 DRM_MEM_DRIVER);
+	   	dev->dev_private = NULL;
+
+		for (i = 0; i < dma->buf_count; i++) {
+			drm_buf_t *buf = dma->buflist[ i ];
+			drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+			drm_ioremapfree(buf_priv->kernel_virtual, buf->total);
+		}
+	}
+   	return 0;
+}
+
+static int i810_wait_ring(drm_device_t *dev, int n)
+{
+   	drm_i810_private_t *dev_priv = dev->dev_private;
+   	drm_i810_ring_buffer_t *ring = &(dev_priv->ring);
+   	int iters = 0;
+   	unsigned long end;
+	unsigned int last_head = I810_READ(LP_RING + RING_HEAD) & HEAD_ADDR;
+
+	end = jiffies + (HZ*3);
+   	while (ring->space < n) {
+	   	int i;
+	
+	   	ring->head = I810_READ(LP_RING + RING_HEAD) & HEAD_ADDR;
+	   	ring->space = ring->head - (ring->tail+8);
+		if (ring->space < 0) ring->space += ring->Size;
+	   
+		if (ring->head != last_head)
+		   end = jiffies + (HZ*3);
+	  
+	   	iters++;
+		if((signed)(end - jiffies) <= 0) {
+		   	DRM_ERROR("space: %d wanted %d\n", ring->space, n);
+		   	DRM_ERROR("lockup\n");
+		   	goto out_wait_ring;
+		}
+
+	   	for (i = 0 ; i < 2000 ; i++) ;
+	}
+
+out_wait_ring:   
+   	return iters;
+}
+
+static void i810_kernel_lost_context(drm_device_t *dev)
+{
+      	drm_i810_private_t *dev_priv = dev->dev_private;
+   	drm_i810_ring_buffer_t *ring = &(dev_priv->ring);
+      
+   	ring->head = I810_READ(LP_RING + RING_HEAD) & HEAD_ADDR;
+     	ring->tail = I810_READ(LP_RING + RING_TAIL);
+     	ring->space = ring->head - (ring->tail+8);
+     	if (ring->space < 0) ring->space += ring->Size;
+}
+
+static int i810_freelist_init(drm_device_t *dev)
+{
+      	drm_device_dma_t *dma = dev->dma;
+   	drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+   	int my_idx = 24;
+   	u32 *hw_status = (u32 *)(dev_priv->hw_status_page + my_idx);
+   	int i;
+   
+   	if(dma->buf_count > 1019) {
+	   	/* Not enough space in the status page for the freelist */
+	   	return -EINVAL;
+	}
+
+   	for (i = 0; i < dma->buf_count; i++) {
+	   	drm_buf_t *buf = dma->buflist[ i ];
+	   	drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+	   
+	   	buf_priv->in_use = hw_status++;
+	   	buf_priv->my_use_idx = my_idx;
+	   	my_idx += 4;
+
+	   	*buf_priv->in_use = I810_BUF_FREE;
+
+		buf_priv->kernel_virtual = drm_ioremap(buf->bus_address, 
+						       buf->total);
+	}
+	return 0;
+}
+
+static int i810_dma_initialize(drm_device_t *dev, 
+			       drm_i810_private_t *dev_priv,
+			       drm_i810_init_t *init)
+{
+	drm_map_t *sarea_map;
+
+   	dev->dev_private = (void *) dev_priv;
+   	memset(dev_priv, 0, sizeof(drm_i810_private_t));
+
+   	if (init->ring_map_idx >= dev->map_count ||
+	    init->buffer_map_idx >= dev->map_count) {
+	   	i810_dma_cleanup(dev);
+	   	DRM_ERROR("ring_map or buffer_map are invalid\n");
+	   	return -EINVAL;
+	}
+   
+   	dev_priv->ring_map_idx = init->ring_map_idx;
+   	dev_priv->buffer_map_idx = init->buffer_map_idx;
+	sarea_map = dev->maplist[0];
+	dev_priv->sarea_priv = (drm_i810_sarea_t *) 
+		((u8 *)sarea_map->handle + 
+		 init->sarea_priv_offset);
+
+   	atomic_set(&dev_priv->flush_done, 0);
+	init_waitqueue_head(&dev_priv->flush_queue);
+   	
+   	dev_priv->ring.Start = init->ring_start;
+   	dev_priv->ring.End = init->ring_end;
+   	dev_priv->ring.Size = init->ring_size;
+
+   	dev_priv->ring.virtual_start = drm_ioremap(dev->agp->base + 
+						   init->ring_start, 
+						   init->ring_size);
+
+   	dev_priv->ring.tail_mask = dev_priv->ring.Size - 1;
+   
+   	if (dev_priv->ring.virtual_start == NULL) {
+	   	i810_dma_cleanup(dev);
+	   	DRM_ERROR("can not ioremap virtual address for"
+			  " ring buffer\n");
+	   	return -ENOMEM;
+	}
+
+	dev_priv->w = init->w;
+	dev_priv->h = init->h;
+	dev_priv->pitch = init->pitch;
+	dev_priv->back_offset = init->back_offset;
+	dev_priv->depth_offset = init->depth_offset;
+
+	dev_priv->front_di1 = init->front_offset | init->pitch_bits;
+	dev_priv->back_di1 = init->back_offset | init->pitch_bits;
+	dev_priv->zi1 = init->depth_offset | init->pitch_bits;
+	
+   
+   	/* Program Hardware Status Page */
+   	dev_priv->hw_status_page = i810_alloc_page(dev);
+   	memset((void *) dev_priv->hw_status_page, 0, PAGE_SIZE);
+   	if(dev_priv->hw_status_page == 0UL) {
+		i810_dma_cleanup(dev);
+		DRM_ERROR("Can not allocate hardware status page\n");
+		return -ENOMEM;
+	}
+   	DRM_DEBUG("hw status page @ %lx\n", dev_priv->hw_status_page);
+   
+   	I810_WRITE(0x02080, virt_to_bus((void *)dev_priv->hw_status_page));
+   	DRM_DEBUG("Enabled hardware status page\n");
+   
+   	/* Now we need to init our freelist */
+   	if(i810_freelist_init(dev) != 0) {
+	   	i810_dma_cleanup(dev);
+	   	DRM_ERROR("Not enough space in the status page for"
+			  " the freelist\n");
+	   	return -ENOMEM;
+	}
+   	return 0;
+}
+
+int i810_dma_init(struct inode *inode, struct file *filp,
+		  unsigned int cmd, unsigned long arg)
+{
+   	drm_file_t *priv = filp->private_data;
+   	drm_device_t *dev = priv->dev;
+   	drm_i810_private_t *dev_priv;
+   	drm_i810_init_t init;
+   	int retcode = 0;
+	
+  	if (copy_from_user(&init, (drm_i810_init_t *)arg, sizeof(init)))
+		return -EFAULT;
+	
+   	switch(init.func) {
+	 	case I810_INIT_DMA:
+	   		dev_priv = drm_alloc(sizeof(drm_i810_private_t), 
+					     DRM_MEM_DRIVER);
+	   		if(dev_priv == NULL) return -ENOMEM;
+	   		retcode = i810_dma_initialize(dev, dev_priv, &init);
+	   	break;
+	 	case I810_CLEANUP_DMA:
+	   		retcode = i810_dma_cleanup(dev);
+	   	break;
+	 	default:
+	   		retcode = -EINVAL;
+	   	break;
+	}
+   
+   	return retcode;
+}
+
+
+
+/* Most efficient way to verify state for the i810 is as it is
+ * emitted.  Non-conformant state is silently dropped.
+ *
+ * Use 'volatile' & local var tmp to force the emitted values to be
+ * identical to the verified ones.
+ */
+static void i810EmitContextVerified( drm_device_t *dev, 
+				     volatile unsigned int *code ) 
+{	
+   	drm_i810_private_t *dev_priv = dev->dev_private;
+	int i, j = 0;
+	unsigned int tmp;
+	RING_LOCALS;
+
+	BEGIN_LP_RING( I810_CTX_SETUP_SIZE );
+
+	OUT_RING( GFX_OP_COLOR_FACTOR );
+	OUT_RING( code[I810_CTXREG_CF1] );
+
+	OUT_RING( GFX_OP_STIPPLE );
+	OUT_RING( code[I810_CTXREG_ST1] );
+
+	for ( i = 4 ; i < I810_CTX_SETUP_SIZE ; i++ ) {
+		tmp = code[i];
+
+		if ((tmp & (7<<29)) == (3<<29) &&
+		    (tmp & (0x1f<<24)) < (0x1d<<24)) 
+		{
+			OUT_RING( tmp ); 
+			j++;
+		} 
+	}
+
+	if (j & 1) 
+		OUT_RING( 0 ); 
+
+	ADVANCE_LP_RING();
+}
+
+static void i810EmitTexVerified( drm_device_t *dev, 
+				 volatile unsigned int *code ) 
+{	
+   	drm_i810_private_t *dev_priv = dev->dev_private;
+	int i, j = 0;
+	unsigned int tmp;
+	RING_LOCALS;
+
+	BEGIN_LP_RING( I810_TEX_SETUP_SIZE );
+
+	OUT_RING( GFX_OP_MAP_INFO );
+	OUT_RING( code[I810_TEXREG_MI1] );
+	OUT_RING( code[I810_TEXREG_MI2] );
+	OUT_RING( code[I810_TEXREG_MI3] );
+
+	for ( i = 4 ; i < I810_TEX_SETUP_SIZE ; i++ ) {
+		tmp = code[i];
+
+		if ((tmp & (7<<29)) == (3<<29) &&
+		    (tmp & (0x1f<<24)) < (0x1d<<24)) 
+		{
+			OUT_RING( tmp ); 
+			j++;
+		}
+	} 
+		
+	if (j & 1) 
+		OUT_RING( 0 ); 
+
+	ADVANCE_LP_RING();
+}
+
+
+/* Need to do some additional checking when setting the dest buffer.
+ */
+static void i810EmitDestVerified( drm_device_t *dev, 
+				  volatile unsigned int *code ) 
+{	
+   	drm_i810_private_t *dev_priv = dev->dev_private;
+	unsigned int tmp;
+	RING_LOCALS;
+
+	BEGIN_LP_RING( I810_DEST_SETUP_SIZE + 2 );
+
+	tmp = code[I810_DESTREG_DI1];
+	if (tmp == dev_priv->front_di1 || tmp == dev_priv->back_di1) {
+		OUT_RING( CMD_OP_DESTBUFFER_INFO );
+		OUT_RING( tmp );
+	} else
+	   DRM_DEBUG("bad di1 %x (allow %x or %x)\n",
+		     tmp, dev_priv->front_di1, dev_priv->back_di1);
+
+	/* invarient:
+	 */
+	OUT_RING( CMD_OP_Z_BUFFER_INFO );
+	OUT_RING( dev_priv->zi1 );
+
+	OUT_RING( GFX_OP_DESTBUFFER_VARS );
+	OUT_RING( code[I810_DESTREG_DV1] );
+
+	OUT_RING( GFX_OP_DRAWRECT_INFO );
+	OUT_RING( code[I810_DESTREG_DR1] );
+	OUT_RING( code[I810_DESTREG_DR2] );
+	OUT_RING( code[I810_DESTREG_DR3] );
+	OUT_RING( code[I810_DESTREG_DR4] );
+	OUT_RING( 0 );
+
+	ADVANCE_LP_RING();
+}
+
+
+
+static void i810EmitState( drm_device_t *dev )
+{
+   	drm_i810_private_t *dev_priv = dev->dev_private;
+      	drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int dirty = sarea_priv->dirty;
+
+	if (dirty & I810_UPLOAD_BUFFERS) {
+		i810EmitDestVerified( dev, sarea_priv->BufferState );
+		sarea_priv->dirty &= ~I810_UPLOAD_BUFFERS;
+	}
+
+	if (dirty & I810_UPLOAD_CTX) {
+		i810EmitContextVerified( dev, sarea_priv->ContextState );
+		sarea_priv->dirty &= ~I810_UPLOAD_CTX;
+	}
+
+	if (dirty & I810_UPLOAD_TEX0) {
+		i810EmitTexVerified( dev, sarea_priv->TexState[0] );
+		sarea_priv->dirty &= ~I810_UPLOAD_TEX0;
+	}
+
+	if (dirty & I810_UPLOAD_TEX1) {
+		i810EmitTexVerified( dev, sarea_priv->TexState[1] );
+		sarea_priv->dirty &= ~I810_UPLOAD_TEX1;
+	}
+}
+
+
+
+/* need to verify 
+ */
+static void i810_dma_dispatch_clear( drm_device_t *dev, int flags, 
+				     unsigned int clear_color,
+				     unsigned int clear_zval )
+{
+   	drm_i810_private_t *dev_priv = dev->dev_private;
+      	drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	int nbox = sarea_priv->nbox;
+	drm_clip_rect_t *pbox = sarea_priv->boxes;
+	int pitch = dev_priv->pitch;
+	int cpp = 2;
+	int i;
+	RING_LOCALS;
+
+  	i810_kernel_lost_context(dev);
+
+      	if (nbox > I810_NR_SAREA_CLIPRECTS)
+     		nbox = I810_NR_SAREA_CLIPRECTS;
+
+	for (i = 0 ; i < nbox ; i++, pbox++) {
+		unsigned int x = pbox->x1;
+		unsigned int y = pbox->y1;
+		unsigned int width = (pbox->x2 - x) * cpp;
+		unsigned int height = pbox->y2 - y;
+		unsigned int start = y * pitch + x * cpp;
+
+		if (pbox->x1 > pbox->x2 ||
+		    pbox->y1 > pbox->y2 ||
+		    pbox->x2 > dev_priv->w ||
+		    pbox->y2 > dev_priv->h)
+			continue;
+
+	   	if ( flags & I810_FRONT ) {	    
+		   	DRM_DEBUG("clear front\n");
+			BEGIN_LP_RING( 6 );	    
+			OUT_RING( BR00_BITBLT_CLIENT | 
+				  BR00_OP_COLOR_BLT | 0x3 );
+			OUT_RING( BR13_SOLID_PATTERN | (0xF0 << 16) | pitch );
+			OUT_RING( (height << 16) | width );
+			OUT_RING( start );
+			OUT_RING( clear_color );
+			OUT_RING( 0 );
+			ADVANCE_LP_RING();
+		}
+
+		if ( flags & I810_BACK ) {
+			DRM_DEBUG("clear back\n");
+			BEGIN_LP_RING( 6 );	    
+			OUT_RING( BR00_BITBLT_CLIENT | 
+				  BR00_OP_COLOR_BLT | 0x3 );
+			OUT_RING( BR13_SOLID_PATTERN | (0xF0 << 16) | pitch );
+			OUT_RING( (height << 16) | width );
+			OUT_RING( dev_priv->back_offset + start );
+			OUT_RING( clear_color );
+			OUT_RING( 0 );
+			ADVANCE_LP_RING();
+		}
+
+		if ( flags & I810_DEPTH ) {
+			DRM_DEBUG("clear depth\n");
+			BEGIN_LP_RING( 6 );	    
+			OUT_RING( BR00_BITBLT_CLIENT | 
+				  BR00_OP_COLOR_BLT | 0x3 );
+			OUT_RING( BR13_SOLID_PATTERN | (0xF0 << 16) | pitch );
+			OUT_RING( (height << 16) | width );
+			OUT_RING( dev_priv->depth_offset + start );
+			OUT_RING( clear_zval );
+			OUT_RING( 0 );
+			ADVANCE_LP_RING();
+		}
+	}
+}
+
+static void i810_dma_dispatch_swap( drm_device_t *dev )
+{
+   	drm_i810_private_t *dev_priv = dev->dev_private;
+      	drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	int nbox = sarea_priv->nbox;
+	drm_clip_rect_t *pbox = sarea_priv->boxes;
+	int pitch = dev_priv->pitch;
+	int cpp = 2;
+	int ofs = dev_priv->back_offset;
+	int i;
+	RING_LOCALS;
+
+	DRM_DEBUG("swapbuffers\n");
+
+  	i810_kernel_lost_context(dev);
+
+      	if (nbox > I810_NR_SAREA_CLIPRECTS)
+     		nbox = I810_NR_SAREA_CLIPRECTS;
+
+	for (i = 0 ; i < nbox; i++, pbox++) 
+	{
+		unsigned int w = pbox->x2 - pbox->x1;
+		unsigned int h = pbox->y2 - pbox->y1;
+		unsigned int dst = pbox->x1*cpp + pbox->y1*pitch;
+		unsigned int start = ofs + dst;
+
+		if (pbox->x1 > pbox->x2 ||
+		    pbox->y1 > pbox->y2 ||
+		    pbox->x2 > dev_priv->w ||
+		    pbox->y2 > dev_priv->h)
+			continue;
+ 
+	   	DRM_DEBUG("dispatch swap %d,%d-%d,%d!\n",
+			  pbox[i].x1, pbox[i].y1,
+			  pbox[i].x2, pbox[i].y2);
+
+		BEGIN_LP_RING( 6 );
+		OUT_RING( BR00_BITBLT_CLIENT | BR00_OP_SRC_COPY_BLT | 0x4 );
+		OUT_RING( pitch | (0xCC << 16));
+		OUT_RING( (h << 16) | (w * cpp));
+		OUT_RING( dst );
+		OUT_RING( pitch );	
+		OUT_RING( start );
+		ADVANCE_LP_RING();
+	}
+}
+
+
+static void i810_dma_dispatch_vertex(drm_device_t *dev, 
+				     drm_buf_t *buf,
+				     int discard,
+				     int used)
+{
+   	drm_i810_private_t *dev_priv = dev->dev_private;
+	drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+   	drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv;
+   	drm_clip_rect_t *box = sarea_priv->boxes;
+   	int nbox = sarea_priv->nbox;
+	unsigned long address = (unsigned long)buf->bus_address;
+	unsigned long start = address - dev->agp->base;     
+	int i = 0, u;
+   	RING_LOCALS;
+
+   	i810_kernel_lost_context(dev);
+
+   	if (nbox > I810_NR_SAREA_CLIPRECTS) 
+		nbox = I810_NR_SAREA_CLIPRECTS;
+
+	if (discard) {
+		u = cmpxchg(buf_priv->in_use, I810_BUF_CLIENT, 
+			    I810_BUF_HARDWARE);
+		if(u != I810_BUF_CLIENT) {
+			DRM_DEBUG("xxxx 2\n");
+		}
+	}
+
+	if (used > 4*1024) 
+		used = 0;
+
+	if (sarea_priv->dirty)
+	   i810EmitState( dev );
+
+  	DRM_DEBUG("dispatch vertex addr 0x%lx, used 0x%x nbox %d\n", 
+		  address, used, nbox);
+
+   	dev_priv->counter++;
+   	DRM_DEBUG(  "dispatch counter : %ld\n", dev_priv->counter);
+   	DRM_DEBUG(  "i810_dma_dispatch\n");
+   	DRM_DEBUG(  "start : %lx\n", start);
+	DRM_DEBUG(  "used : %d\n", used);
+   	DRM_DEBUG(  "start + used - 4 : %ld\n", start + used - 4);
+
+	if (buf_priv->currently_mapped == I810_BUF_MAPPED) {
+		*(u32 *)buf_priv->virtual = (GFX_OP_PRIMITIVE |
+					     sarea_priv->vertex_prim |
+					     ((used/4)-2));
+		
+		if (used & 4) {
+			*(u32 *)((u32)buf_priv->virtual + used) = 0;
+			used += 4;
+		}
+
+		i810_unmap_buffer(buf);
+	}
+		   
+	if (used) {
+		do {
+			if (i < nbox) {
+				BEGIN_LP_RING(4);
+				OUT_RING( GFX_OP_SCISSOR | SC_UPDATE_SCISSOR | 
+					  SC_ENABLE );
+				OUT_RING( GFX_OP_SCISSOR_INFO );
+				OUT_RING( box[i].x1 | (box[i].y1<<16) );
+				OUT_RING( (box[i].x2-1) | ((box[i].y2-1)<<16) );
+				ADVANCE_LP_RING();
+			}
+			
+			BEGIN_LP_RING(4);
+			OUT_RING( CMD_OP_BATCH_BUFFER );
+			OUT_RING( start | BB1_PROTECTED );
+			OUT_RING( start + used - 4 );
+			OUT_RING( 0 );
+			ADVANCE_LP_RING();
+			
+		} while (++i < nbox);
+	}
+
+	BEGIN_LP_RING(10);
+	OUT_RING( CMD_STORE_DWORD_IDX );
+	OUT_RING( 20 );
+	OUT_RING( dev_priv->counter );
+	OUT_RING( 0 );
+
+	if (discard) {
+		OUT_RING( CMD_STORE_DWORD_IDX );
+		OUT_RING( buf_priv->my_use_idx );
+		OUT_RING( I810_BUF_FREE );
+		OUT_RING( 0 );
+	}
+
+      	OUT_RING( CMD_REPORT_HEAD );
+	OUT_RING( 0 );
+   	ADVANCE_LP_RING();
+}
+
+
+/* Interrupts are only for flushing */
+static void i810_dma_service(int irq, void *device, struct pt_regs *regs)
+{
+	drm_device_t	 *dev = (drm_device_t *)device;
+   	u16 temp;
+   
+	atomic_inc(&dev->total_irq);
+      	temp = I810_READ16(I810REG_INT_IDENTITY_R);
+   	temp = temp & ~(0x6000);
+   	if(temp != 0) I810_WRITE16(I810REG_INT_IDENTITY_R, 
+				   temp); /* Clear all interrupts */
+	else
+	   return;
+ 
+   	queue_task(&dev->tq, &tq_immediate);
+   	mark_bh(IMMEDIATE_BH);
+}
+
+static void i810_dma_task_queue(void *device)
+{
+	drm_device_t *dev = (drm_device_t *) device;
+      	drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+
+   	atomic_set(&dev_priv->flush_done, 1);
+   	wake_up_interruptible(&dev_priv->flush_queue);
+}
+
+int i810_irq_install(drm_device_t *dev, int irq)
+{
+	int retcode;
+	u16 temp;
+   
+	if (!irq)     return -EINVAL;
+	
+	down(&dev->struct_sem);
+	if (dev->irq) {
+		up(&dev->struct_sem);
+		return -EBUSY;
+	}
+	dev->irq = irq;
+	up(&dev->struct_sem);
+	
+   	DRM_DEBUG(  "Interrupt Install : %d\n", irq);
+	DRM_DEBUG("%d\n", irq);
+
+	dev->context_flag     = 0;
+	dev->interrupt_flag   = 0;
+	dev->dma_flag	      = 0;
+	
+	dev->dma->next_buffer = NULL;
+	dev->dma->next_queue  = NULL;
+	dev->dma->this_buffer = NULL;
+
+	INIT_LIST_HEAD(&dev->tq.list);
+	dev->tq.sync	      = 0;
+	dev->tq.routine	      = i810_dma_task_queue;
+	dev->tq.data	      = dev;
+
+				/* Before installing handler */
+   	temp = I810_READ16(I810REG_HWSTAM);
+   	temp = temp & 0x6000;
+   	I810_WRITE16(I810REG_HWSTAM, temp);
+   	
+      	temp = I810_READ16(I810REG_INT_MASK_R);
+   	temp = temp & 0x6000;
+   	I810_WRITE16(I810REG_INT_MASK_R, temp); /* Unmask interrupts */
+   	temp = I810_READ16(I810REG_INT_ENABLE_R);
+   	temp = temp & 0x6000;
+      	I810_WRITE16(I810REG_INT_ENABLE_R, temp); /* Disable all interrupts */
+
+				/* Install handler */
+	if ((retcode = request_irq(dev->irq,
+				   i810_dma_service,
+				   SA_SHIRQ,
+				   dev->devname,
+				   dev))) {
+		down(&dev->struct_sem);
+		dev->irq = 0;
+		up(&dev->struct_sem);
+		return retcode;
+	}
+   	temp = I810_READ16(I810REG_INT_ENABLE_R);
+   	temp = temp & 0x6000;
+   	temp = temp | 0x0003;
+   	I810_WRITE16(I810REG_INT_ENABLE_R, 
+		     temp); /* Enable bp & user interrupts */
+	return 0;
+}
+
+int i810_irq_uninstall(drm_device_t *dev)
+{
+	int irq;
+   	u16 temp;
+
+
+/*  	return 0; */
+
+	down(&dev->struct_sem);
+	irq	 = dev->irq;
+	dev->irq = 0;
+	up(&dev->struct_sem);
+	
+	if (!irq) return -EINVAL;
+
+   	DRM_DEBUG(  "Interrupt UnInstall: %d\n", irq);	
+	DRM_DEBUG("%d\n", irq);
+   
+   	temp = I810_READ16(I810REG_INT_IDENTITY_R);
+   	temp = temp & ~(0x6000);
+   	if(temp != 0) I810_WRITE16(I810REG_INT_IDENTITY_R, 
+				   temp); /* Clear all interrupts */
+   
+   	temp = I810_READ16(I810REG_INT_ENABLE_R);
+   	temp = temp & 0x6000;
+   	I810_WRITE16(I810REG_INT_ENABLE_R, 
+		     temp);                     /* Disable all interrupts */
+
+   	free_irq(irq, dev);
+
+	return 0;
+}
+
+int i810_control(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_control_t	ctl;
+	int		retcode;
+   
+   	DRM_DEBUG(  "i810_control\n");
+
+	if (copy_from_user(&ctl, (drm_control_t *)arg, sizeof(ctl)))
+		return -EFAULT;
+	
+	switch (ctl.func) {
+	case DRM_INST_HANDLER:
+		if ((retcode = i810_irq_install(dev, ctl.irq)))
+			return retcode;
+		break;
+	case DRM_UNINST_HANDLER:
+		if ((retcode = i810_irq_uninstall(dev)))
+			return retcode;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static inline void i810_dma_emit_flush(drm_device_t *dev)
+{
+   	drm_i810_private_t *dev_priv = dev->dev_private;
+   	RING_LOCALS;
+
+   	i810_kernel_lost_context(dev);
+
+   	BEGIN_LP_RING(2);
+      	OUT_RING( CMD_REPORT_HEAD );
+      	OUT_RING( GFX_OP_USER_INTERRUPT );
+      	ADVANCE_LP_RING();
+
+/*  	i810_wait_ring( dev, dev_priv->ring.Size - 8 ); */
+/*     	atomic_set(&dev_priv->flush_done, 1); */
+/*     	wake_up_interruptible(&dev_priv->flush_queue); */
+}
+
+static inline void i810_dma_quiescent_emit(drm_device_t *dev)
+{
+      	drm_i810_private_t *dev_priv = dev->dev_private;
+   	RING_LOCALS;
+
+  	i810_kernel_lost_context(dev);
+
+   	BEGIN_LP_RING(4);
+   	OUT_RING( INST_PARSER_CLIENT | INST_OP_FLUSH | INST_FLUSH_MAP_CACHE );
+   	OUT_RING( CMD_REPORT_HEAD );
+      	OUT_RING( 0 );
+      	OUT_RING( GFX_OP_USER_INTERRUPT );
+   	ADVANCE_LP_RING();
+
+/*  	i810_wait_ring( dev, dev_priv->ring.Size - 8 ); */
+/*     	atomic_set(&dev_priv->flush_done, 1); */
+/*     	wake_up_interruptible(&dev_priv->flush_queue); */
+}
+
+static void i810_dma_quiescent(drm_device_t *dev)
+{
+      	DECLARE_WAITQUEUE(entry, current);
+  	drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+	unsigned long end;      
+
+   	if(dev_priv == NULL) {
+	   	return;
+	}
+      	atomic_set(&dev_priv->flush_done, 0);
+   	add_wait_queue(&dev_priv->flush_queue, &entry);
+   	end = jiffies + (HZ*3);
+   
+   	for (;;) {
+		current->state = TASK_INTERRUPTIBLE;
+	      	i810_dma_quiescent_emit(dev);
+	   	if (atomic_read(&dev_priv->flush_done) == 1) break;
+		if((signed)(end - jiffies) <= 0) {
+		   	DRM_ERROR("lockup\n");
+		   	break;
+		}	   
+	      	schedule_timeout(HZ*3);
+	      	if (signal_pending(current)) {
+		   	break;
+		}
+	}
+   
+   	current->state = TASK_RUNNING;
+   	remove_wait_queue(&dev_priv->flush_queue, &entry);
+   
+   	return;
+}
+
+static int i810_flush_queue(drm_device_t *dev)
+{
+   	DECLARE_WAITQUEUE(entry, current);
+  	drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+	drm_device_dma_t *dma = dev->dma;
+	unsigned long end;
+   	int i, ret = 0;      
+
+   	if(dev_priv == NULL) {
+	   	return 0;
+	}
+      	atomic_set(&dev_priv->flush_done, 0);
+   	add_wait_queue(&dev_priv->flush_queue, &entry);
+   	end = jiffies + (HZ*3);
+   	for (;;) {
+		current->state = TASK_INTERRUPTIBLE;
+	      	i810_dma_emit_flush(dev);
+	   	if (atomic_read(&dev_priv->flush_done) == 1) break;
+		if((signed)(end - jiffies) <= 0) {
+		   	DRM_ERROR("lockup\n");
+		   	break;
+		}	   
+	      	schedule_timeout(HZ*3);
+	      	if (signal_pending(current)) {
+		   	ret = -EINTR; /* Can't restart */
+		   	break;
+		}
+	}
+   
+   	current->state = TASK_RUNNING;
+   	remove_wait_queue(&dev_priv->flush_queue, &entry);
+
+
+   	for (i = 0; i < dma->buf_count; i++) {
+	   	drm_buf_t *buf = dma->buflist[ i ];
+	   	drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+	   
+		int used = cmpxchg(buf_priv->in_use, I810_BUF_HARDWARE, 
+				   I810_BUF_FREE);
+
+		if (used == I810_BUF_HARDWARE)
+			DRM_DEBUG("reclaimed from HARDWARE\n");
+		if (used == I810_BUF_CLIENT)
+			DRM_DEBUG("still on client HARDWARE\n");
+	}
+
+   	return ret;
+}
+
+/* Must be called with the lock held */
+void i810_reclaim_buffers(drm_device_t *dev, pid_t pid)
+{
+	drm_device_dma_t *dma = dev->dma;
+	int		 i;
+
+	if (!dma) return;
+      	if (!dev->dev_private) return;
+	if (!dma->buflist) return;
+
+        i810_flush_queue(dev);
+
+	for (i = 0; i < dma->buf_count; i++) {
+	   	drm_buf_t *buf = dma->buflist[ i ];
+	   	drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+	   
+		if (buf->pid == pid && buf_priv) {
+			int used = cmpxchg(buf_priv->in_use, I810_BUF_CLIENT, 
+					   I810_BUF_FREE);
+
+			if (used == I810_BUF_CLIENT)
+				DRM_DEBUG("reclaimed from client\n");
+		   	if(buf_priv->currently_mapped == I810_BUF_MAPPED)
+		     		buf_priv->currently_mapped = I810_BUF_UNMAPPED;
+		}
+	}
+}
+
+int i810_lock(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+
+	DECLARE_WAITQUEUE(entry, current);
+	int		  ret	= 0;
+	drm_lock_t	  lock;
+
+	if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)))
+		return -EFAULT;
+
+	if (lock.context == DRM_KERNEL_CONTEXT) {
+		DRM_ERROR("Process %d using kernel context %d\n",
+			  current->pid, lock.context);
+		return -EINVAL;
+	}
+   
+   	DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
+		  lock.context, current->pid, dev->lock.hw_lock->lock,
+		  lock.flags);
+
+	if (lock.context < 0) {
+		return -EINVAL;
+	}
+	/* Only one queue:
+	 */
+
+	if (!ret) {
+		add_wait_queue(&dev->lock.lock_queue, &entry);
+		for (;;) {
+			current->state = TASK_INTERRUPTIBLE;
+			if (!dev->lock.hw_lock) {
+				/* Device has been unregistered */
+				ret = -EINTR;
+				break;
+			}
+			if (drm_lock_take(&dev->lock.hw_lock->lock,
+					  lock.context)) {
+				dev->lock.pid	    = current->pid;
+				dev->lock.lock_time = jiffies;
+				atomic_inc(&dev->total_locks);
+				break;	/* Got lock */
+			}
+			
+				/* Contention */
+			atomic_inc(&dev->total_sleeps);
+		   	DRM_DEBUG("Calling lock schedule\n");
+			schedule();
+			if (signal_pending(current)) {
+				ret = -ERESTARTSYS;
+				break;
+			}
+		}
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&dev->lock.lock_queue, &entry);
+	}
+	
+	if (!ret) {
+		sigemptyset(&dev->sigmask);
+		sigaddset(&dev->sigmask, SIGSTOP);
+		sigaddset(&dev->sigmask, SIGTSTP);
+		sigaddset(&dev->sigmask, SIGTTIN);
+		sigaddset(&dev->sigmask, SIGTTOU);
+		dev->sigdata.context = lock.context;
+		dev->sigdata.lock    = dev->lock.hw_lock;
+		block_all_signals(drm_notifier, &dev->sigdata, &dev->sigmask);
+
+		if (lock.flags & _DRM_LOCK_QUIESCENT) {
+		   DRM_DEBUG("_DRM_LOCK_QUIESCENT\n");
+		   DRM_DEBUG("fred\n");
+		   i810_dma_quiescent(dev);
+		}
+	}
+	DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
+	return ret;
+}
+
+int i810_flush_ioctl(struct inode *inode, struct file *filp, 
+		     unsigned int cmd, unsigned long arg)
+{
+   	drm_file_t	  *priv	  = filp->private_data;
+   	drm_device_t	  *dev	  = priv->dev;
+   
+   	DRM_DEBUG("i810_flush_ioctl\n");
+   	if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("i810_flush_ioctl called without lock held\n");
+		return -EINVAL;
+	}
+
+   	i810_flush_queue(dev);
+   	return 0;
+}
+
+
+int i810_dma_vertex(struct inode *inode, struct file *filp,
+	       unsigned int cmd, unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_device_dma_t *dma = dev->dma;
+   	drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+      	u32 *hw_status = (u32 *)dev_priv->hw_status_page;
+   	drm_i810_sarea_t *sarea_priv = (drm_i810_sarea_t *) 
+     					dev_priv->sarea_priv; 
+	drm_i810_vertex_t vertex;
+
+	if (copy_from_user(&vertex, (drm_i810_vertex_t *)arg, sizeof(vertex)))
+		return -EFAULT;
+
+   	if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("i810_dma_vertex called without lock held\n");
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("i810 dma vertex, idx %d used %d discard %d\n",
+		  vertex.idx, vertex.used, vertex.discard);
+
+	if(vertex.idx < 0 || vertex.idx > dma->buf_count) return -EINVAL;
+
+	i810_dma_dispatch_vertex( dev, 
+				  dma->buflist[ vertex.idx ], 
+				  vertex.discard, vertex.used );
+
+   	atomic_add(vertex.used, &dma->total_bytes);
+	atomic_inc(&dma->total_dmas);
+	sarea_priv->last_enqueue = dev_priv->counter-1;
+   	sarea_priv->last_dispatch = (int) hw_status[5];
+   
+	return 0;
+}
+
+
+
+int i810_clear_bufs(struct inode *inode, struct file *filp,
+		   unsigned int cmd, unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_i810_clear_t clear;
+
+   	if (copy_from_user(&clear, (drm_i810_clear_t *)arg, sizeof(clear)))
+		return -EFAULT;
+   
+   	if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("i810_clear_bufs called without lock held\n");
+		return -EINVAL;
+	}
+
+	i810_dma_dispatch_clear( dev, clear.flags, 
+				 clear.clear_color, 
+				 clear.clear_depth );
+   	return 0;
+}
+
+int i810_swap_bufs(struct inode *inode, struct file *filp,
+		  unsigned int cmd, unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+   
+	DRM_DEBUG("i810_swap_bufs\n");
+
+   	if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("i810_swap_buf called without lock held\n");
+		return -EINVAL;
+	}
+
+	i810_dma_dispatch_swap( dev );
+   	return 0;
+}
+
+int i810_getage(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+   	drm_file_t	  *priv	    = filp->private_data;
+	drm_device_t	  *dev	    = priv->dev;
+   	drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+      	u32 *hw_status = (u32 *)dev_priv->hw_status_page;
+   	drm_i810_sarea_t *sarea_priv = (drm_i810_sarea_t *) 
+     					dev_priv->sarea_priv; 
+
+      	sarea_priv->last_dispatch = (int) hw_status[5];
+	return 0;
+}
+
+int i810_getbuf(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_file_t	  *priv	    = filp->private_data;
+	drm_device_t	  *dev	    = priv->dev;
+	int		  retcode   = 0;
+	drm_i810_dma_t	  d;
+   	drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+   	u32 *hw_status = (u32 *)dev_priv->hw_status_page;
+   	drm_i810_sarea_t *sarea_priv = (drm_i810_sarea_t *) 
+     					dev_priv->sarea_priv; 
+
+	DRM_DEBUG("getbuf\n");
+   	if (copy_from_user(&d, (drm_i810_dma_t *)arg, sizeof(d)))
+		return -EFAULT;
+   
+	if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("i810_dma called without lock held\n");
+		return -EINVAL;
+	}
+	
+	d.granted = 0;
+
+	retcode = i810_dma_get_buffer(dev, &d, filp);
+
+	DRM_DEBUG("i810_dma: %d returning %d, granted = %d\n",
+		  current->pid, retcode, d.granted);
+
+	if (copy_to_user((drm_dma_t *)arg, &d, sizeof(d)))
+		return -EFAULT;
+   	sarea_priv->last_dispatch = (int) hw_status[5];
+
+	return retcode;
+}
+
+int i810_copybuf(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_file_t	  *priv	    = filp->private_data;
+	drm_device_t	  *dev	    = priv->dev;
+	drm_i810_copy_t	  d;
+   	drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+   	u32 *hw_status = (u32 *)dev_priv->hw_status_page;
+   	drm_i810_sarea_t *sarea_priv = (drm_i810_sarea_t *) 
+     					dev_priv->sarea_priv; 
+	drm_buf_t *buf;
+	drm_i810_buf_priv_t *buf_priv;
+	drm_device_dma_t *dma = dev->dma;
+
+	if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("i810_dma called without lock held\n");
+		return -EINVAL;
+	}
+   
+   	if (copy_from_user(&d, (drm_i810_copy_t *)arg, sizeof(d)))
+		return -EFAULT;
+
+	if(d.idx < 0 || d.idx > dma->buf_count) return -EINVAL;
+	buf = dma->buflist[ d.idx ];
+   	buf_priv = buf->dev_private;
+	if (buf_priv->currently_mapped != I810_BUF_MAPPED) return -EPERM;
+
+	/* Stopping end users copying their data to the entire kernel
+	   is good.. */
+	if (d.used < 0 || d.used > buf->total)
+		return -EINVAL;
+		
+   	if (copy_from_user(buf_priv->virtual, d.address, d.used))
+		return -EFAULT;
+
+   	sarea_priv->last_dispatch = (int) hw_status[5];
+
+	return 0;
+}
+
+int i810_docopy(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	if(VM_DONTCOPY == 0) return 1;
+	return 0;
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/i810_drm.h linux/drivers/char/drm-4.0/i810_drm.h
--- linux-2.4.18-pre2/drivers/char/drm-4.0/i810_drm.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/i810_drm.h	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,194 @@
+#ifndef _I810_DRM_H_
+#define _I810_DRM_H_
+
+/* WARNING: These defines must be the same as what the Xserver uses.
+ * if you change them, you must change the defines in the Xserver.
+ */
+
+#ifndef _I810_DEFINES_
+#define _I810_DEFINES_
+
+#define I810_DMA_BUF_ORDER		12
+#define I810_DMA_BUF_SZ 		(1<<I810_DMA_BUF_ORDER)
+#define I810_DMA_BUF_NR 		256
+#define I810_NR_SAREA_CLIPRECTS 	8
+
+/* Each region is a minimum of 64k, and there are at most 64 of them.
+ */
+#define I810_NR_TEX_REGIONS 64
+#define I810_LOG_MIN_TEX_REGION_SIZE 16
+#endif
+
+#define I810_UPLOAD_TEX0IMAGE  0x1 /* handled clientside */
+#define I810_UPLOAD_TEX1IMAGE  0x2 /* handled clientside */
+#define I810_UPLOAD_CTX        0x4
+#define I810_UPLOAD_BUFFERS    0x8
+#define I810_UPLOAD_TEX0       0x10
+#define I810_UPLOAD_TEX1       0x20
+#define I810_UPLOAD_CLIPRECTS  0x40
+
+
+/* Indices into buf.Setup where various bits of state are mirrored per
+ * context and per buffer.  These can be fired at the card as a unit,
+ * or in a piecewise fashion as required.
+ */
+
+/* Destbuffer state 
+ *    - backbuffer linear offset and pitch -- invarient in the current dri
+ *    - zbuffer linear offset and pitch -- also invarient
+ *    - drawing origin in back and depth buffers.
+ *
+ * Keep the depth/back buffer state here to acommodate private buffers
+ * in the future.
+ */
+#define I810_DESTREG_DI0  0	/* CMD_OP_DESTBUFFER_INFO (2 dwords) */
+#define I810_DESTREG_DI1  1
+#define I810_DESTREG_DV0  2	/* GFX_OP_DESTBUFFER_VARS (2 dwords) */
+#define I810_DESTREG_DV1  3
+#define I810_DESTREG_DR0  4	/* GFX_OP_DRAWRECT_INFO (4 dwords) */
+#define I810_DESTREG_DR1  5
+#define I810_DESTREG_DR2  6
+#define I810_DESTREG_DR3  7
+#define I810_DESTREG_DR4  8
+#define I810_DEST_SETUP_SIZE 10
+
+/* Context state
+ */
+#define I810_CTXREG_CF0   0	/* GFX_OP_COLOR_FACTOR */
+#define I810_CTXREG_CF1   1	
+#define I810_CTXREG_ST0   2     /* GFX_OP_STIPPLE */
+#define I810_CTXREG_ST1   3
+#define I810_CTXREG_VF    4	/* GFX_OP_VERTEX_FMT */
+#define I810_CTXREG_MT    5	/* GFX_OP_MAP_TEXELS */
+#define I810_CTXREG_MC0   6	/* GFX_OP_MAP_COLOR_STAGES - stage 0 */
+#define I810_CTXREG_MC1   7     /* GFX_OP_MAP_COLOR_STAGES - stage 1 */
+#define I810_CTXREG_MC2   8	/* GFX_OP_MAP_COLOR_STAGES - stage 2 */
+#define I810_CTXREG_MA0   9	/* GFX_OP_MAP_ALPHA_STAGES - stage 0 */
+#define I810_CTXREG_MA1   10	/* GFX_OP_MAP_ALPHA_STAGES - stage 1 */
+#define I810_CTXREG_MA2   11	/* GFX_OP_MAP_ALPHA_STAGES - stage 2 */
+#define I810_CTXREG_SDM   12	/* GFX_OP_SRC_DEST_MONO */
+#define I810_CTXREG_FOG   13	/* GFX_OP_FOG_COLOR */
+#define I810_CTXREG_B1    14	/* GFX_OP_BOOL_1 */
+#define I810_CTXREG_B2    15	/* GFX_OP_BOOL_2 */
+#define I810_CTXREG_LCS   16	/* GFX_OP_LINEWIDTH_CULL_SHADE_MODE */
+#define I810_CTXREG_PV    17	/* GFX_OP_PV_RULE -- Invarient! */
+#define I810_CTXREG_ZA    18	/* GFX_OP_ZBIAS_ALPHAFUNC */
+#define I810_CTXREG_AA    19	/* GFX_OP_ANTIALIAS */
+#define I810_CTX_SETUP_SIZE 20 
+
+/* Texture state (per tex unit)
+ */
+#define I810_TEXREG_MI0  0	/* GFX_OP_MAP_INFO (4 dwords) */
+#define I810_TEXREG_MI1  1	
+#define I810_TEXREG_MI2  2	
+#define I810_TEXREG_MI3  3	
+#define I810_TEXREG_MF   4	/* GFX_OP_MAP_FILTER */
+#define I810_TEXREG_MLC  5	/* GFX_OP_MAP_LOD_CTL */
+#define I810_TEXREG_MLL  6	/* GFX_OP_MAP_LOD_LIMITS */
+#define I810_TEXREG_MCS  7	/* GFX_OP_MAP_COORD_SETS ??? */
+#define I810_TEX_SETUP_SIZE 8
+
+#define I810_FRONT   0x1
+#define I810_BACK    0x2
+#define I810_DEPTH   0x4
+
+
+typedef struct _drm_i810_init {
+	enum {
+		I810_INIT_DMA = 0x01,
+		I810_CLEANUP_DMA = 0x02
+	} func;
+	int ring_map_idx;
+	int buffer_map_idx;
+	int sarea_priv_offset;
+	unsigned int ring_start;
+	unsigned int ring_end;
+	unsigned int ring_size;
+	unsigned int front_offset;
+	unsigned int back_offset;
+	unsigned int depth_offset;
+	unsigned int w;
+	unsigned int h;
+	unsigned int pitch;
+	unsigned int pitch_bits; 
+} drm_i810_init_t;
+
+/* Warning: If you change the SAREA structure you must change the Xserver
+ * structure as well */
+
+typedef struct _drm_i810_tex_region {
+	unsigned char next, prev; /* indices to form a circular LRU  */
+	unsigned char in_use;	/* owned by a client, or free? */
+	int age;		/* tracked by clients to update local LRU's */
+} drm_i810_tex_region_t;
+
+typedef struct _drm_i810_sarea {
+   	unsigned int ContextState[I810_CTX_SETUP_SIZE];
+   	unsigned int BufferState[I810_DEST_SETUP_SIZE];
+   	unsigned int TexState[2][I810_TEX_SETUP_SIZE];
+   	unsigned int dirty;
+
+	unsigned int nbox;
+	drm_clip_rect_t boxes[I810_NR_SAREA_CLIPRECTS];
+
+	/* Maintain an LRU of contiguous regions of texture space.  If
+	 * you think you own a region of texture memory, and it has an
+	 * age different to the one you set, then you are mistaken and
+	 * it has been stolen by another client.  If global texAge
+	 * hasn't changed, there is no need to walk the list.
+	 *
+	 * These regions can be used as a proxy for the fine-grained
+	 * texture information of other clients - by maintaining them
+	 * in the same lru which is used to age their own textures,
+	 * clients have an approximate lru for the whole of global
+	 * texture space, and can make informed decisions as to which
+	 * areas to kick out.  There is no need to choose whether to
+	 * kick out your own texture or someone else's - simply eject
+	 * them all in LRU order.  
+	 */
+   
+	drm_i810_tex_region_t texList[I810_NR_TEX_REGIONS+1]; 
+				/* Last elt is sentinal */
+        int texAge;		/* last time texture was uploaded */
+        int last_enqueue;	/* last time a buffer was enqueued */
+	int last_dispatch;	/* age of the most recently dispatched buffer */
+	int last_quiescent;     /*  */
+	int ctxOwner;		/* last context to upload state */
+
+	int vertex_prim;
+
+} drm_i810_sarea_t;
+
+typedef struct _drm_i810_clear {
+	int clear_color;
+	int clear_depth;
+	int flags;
+} drm_i810_clear_t;
+
+
+
+/* These may be placeholders if we have more cliprects than
+ * I810_NR_SAREA_CLIPRECTS.  In that case, the client sets discard to
+ * false, indicating that the buffer will be dispatched again with a
+ * new set of cliprects.
+ */
+typedef struct _drm_i810_vertex {
+   	int idx;		/* buffer index */
+	int used;		/* nr bytes in use */
+	int discard;		/* client is finished with the buffer? */
+} drm_i810_vertex_t;
+
+typedef struct _drm_i810_copy_t {
+   	int idx;		/* buffer index */
+	int used;		/* nr bytes in use */
+	void *address;		/* Address to copy from */
+} drm_i810_copy_t;
+
+typedef struct drm_i810_dma {
+	void *virtual;
+	int request_idx;
+	int request_size;
+	int granted;
+} drm_i810_dma_t;
+
+#endif /* _I810_DRM_H_ */
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/i810_drv.c linux/drivers/char/drm-4.0/i810_drv.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/i810_drv.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/i810_drv.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,649 @@
+/* i810_drv.c -- I810 driver -*- linux-c -*-
+ * Created: Mon Dec 13 01:56:22 1999 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
+ *	    Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#include <linux/config.h>
+#include "drmP.h"
+#include "i810_drv.h"
+
+#define I810_NAME	 "i810"
+#define I810_DESC	 "Intel I810"
+#define I810_DATE	 "20000928"
+#define I810_MAJOR	 1
+#define I810_MINOR	 1
+#define I810_PATCHLEVEL	 0
+
+static drm_device_t	      i810_device;
+drm_ctx_t		      i810_res_ctx;
+
+static struct file_operations i810_fops = {
+#if LINUX_VERSION_CODE >= 0x020400
+				/* This started being used during 2.4.0-test */
+	owner:   THIS_MODULE,
+#endif
+	open:	 i810_open,
+	flush:	 drm_flush,
+	release: i810_release,
+	ioctl:	 i810_ioctl,
+	mmap:	 drm_mmap,
+	read:	 drm_read,
+	fasync:	 drm_fasync,
+      	poll:	 drm_poll,
+};
+
+static struct miscdevice      i810_misc = {
+	minor: MISC_DYNAMIC_MINOR,
+	name:  I810_NAME,
+	fops:  &i810_fops,
+};
+
+static drm_ioctl_desc_t	      i810_ioctls[] = {
+	[DRM_IOCTL_NR(DRM_IOCTL_VERSION)]     = { i810_version,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)]  = { drm_getunique,  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)]   = { drm_getmagic,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)]   = { drm_irq_busid,  0, 1 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)]  = { drm_setunique,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_BLOCK)]	      = { drm_block,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)]     = { drm_unblock,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_CONTROL)]     = { i810_control,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)]  = { drm_authmagic,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)]     = { drm_addmap,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)]    = { i810_addbufs,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)]   = { i810_markbufs,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)]   = { i810_infobufs,  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)]   = { i810_freebufs,  1, 0 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)]     = { i810_addctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)]      = { i810_rmctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)]     = { i810_modctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)]     = { i810_getctx,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)]  = { i810_switchctx,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)]     = { i810_newctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)]     = { i810_resctx,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)]    = { drm_adddraw,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)]     = { drm_rmdraw,	  1, 1 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_LOCK)]	      = { i810_lock,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)]      = { i810_unlock,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FINISH)]      = { drm_finish,	  1, 0 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)] = { drm_agp_acquire, 1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)] = { drm_agp_release, 1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)]  = { drm_agp_enable,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)]    = { drm_agp_info,    1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)]   = { drm_agp_alloc,   1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)]    = { drm_agp_free,    1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)]    = { drm_agp_bind,    1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)]  = { drm_agp_unbind,  1, 1 },
+
+   	[DRM_IOCTL_NR(DRM_IOCTL_I810_INIT)]   = { i810_dma_init,   1, 1 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_I810_VERTEX)] = { i810_dma_vertex, 1, 0 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_I810_CLEAR)]  = { i810_clear_bufs, 1, 0 },
+      	[DRM_IOCTL_NR(DRM_IOCTL_I810_FLUSH)]  = { i810_flush_ioctl,1, 0 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_I810_GETAGE)] = { i810_getage,     1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_I810_GETBUF)] = { i810_getbuf,     1, 0 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_I810_SWAP)]   = { i810_swap_bufs,  1, 0 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_I810_COPY)]   = { i810_copybuf,    1, 0 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_I810_DOCOPY)] = { i810_docopy,     1, 0 },
+};
+
+#define I810_IOCTL_COUNT DRM_ARRAY_SIZE(i810_ioctls)
+
+#ifdef MODULE
+static char		      *i810 = NULL;
+#endif
+
+MODULE_AUTHOR("VA Linux Systems, Inc.");
+MODULE_DESCRIPTION("Intel I810");
+MODULE_LICENSE("GPL and additional rights");
+MODULE_PARM(i810, "s");
+
+#ifndef MODULE
+/* i810_options is called by the kernel to parse command-line options
+ * passed via the boot-loader (e.g., LILO).  It calls the insmod option
+ * routine, drm_parse_drm.
+ */
+
+static int __init i810_options(char *str)
+{
+	drm_parse_options(str);
+	return 1;
+}
+
+__setup("i810=", i810_options);
+#endif
+
+static int i810_setup(drm_device_t *dev)
+{
+	int i;
+
+	atomic_set(&dev->ioctl_count, 0);
+	atomic_set(&dev->vma_count, 0);
+	dev->buf_use	  = 0;
+	atomic_set(&dev->buf_alloc, 0);
+
+	drm_dma_setup(dev);
+
+	atomic_set(&dev->total_open, 0);
+	atomic_set(&dev->total_close, 0);
+	atomic_set(&dev->total_ioctl, 0);
+	atomic_set(&dev->total_irq, 0);
+	atomic_set(&dev->total_ctx, 0);
+	atomic_set(&dev->total_locks, 0);
+	atomic_set(&dev->total_unlocks, 0);
+	atomic_set(&dev->total_contends, 0);
+	atomic_set(&dev->total_sleeps, 0);
+
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		dev->magiclist[i].head = NULL;
+		dev->magiclist[i].tail = NULL;
+	}
+	dev->maplist	    = NULL;
+	dev->map_count	    = 0;
+	dev->vmalist	    = NULL;
+	dev->lock.hw_lock   = NULL;
+	init_waitqueue_head(&dev->lock.lock_queue);
+	dev->queue_count    = 0;
+	dev->queue_reserved = 0;
+	dev->queue_slots    = 0;
+	dev->queuelist	    = NULL;
+	dev->irq	    = 0;
+	dev->context_flag   = 0;
+	dev->interrupt_flag = 0;
+	dev->dma_flag	    = 0;
+	dev->last_context   = 0;
+	dev->last_switch    = 0;
+	dev->last_checked   = 0;
+	init_timer(&dev->timer);
+	init_waitqueue_head(&dev->context_wait);
+#if DRM_DMA_HISTO
+	memset(&dev->histo, 0, sizeof(dev->histo));
+#endif
+	dev->ctx_start	    = 0;
+	dev->lck_start	    = 0;
+
+	dev->buf_rp	  = dev->buf;
+	dev->buf_wp	  = dev->buf;
+	dev->buf_end	  = dev->buf + DRM_BSZ;
+	dev->buf_async	  = NULL;
+	init_waitqueue_head(&dev->buf_readers);
+	init_waitqueue_head(&dev->buf_writers);
+
+	DRM_DEBUG("\n");
+
+	/* The kernel's context could be created here, but is now created
+	   in drm_dma_enqueue.	This is more resource-efficient for
+	   hardware that does not do DMA, but may mean that
+	   drm_select_queue fails between the time the interrupt is
+	   initialized and the time the queues are initialized. */
+
+	return 0;
+}
+
+
+static int i810_takedown(drm_device_t *dev)
+{
+	int		  i;
+	drm_magic_entry_t *pt, *next;
+	drm_map_t	  *map;
+	drm_vma_entry_t	  *vma, *vma_next;
+
+	DRM_DEBUG("\n");
+
+	if (dev->irq) i810_irq_uninstall(dev);
+
+	down(&dev->struct_sem);
+	del_timer(&dev->timer);
+
+	if (dev->devname) {
+		drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER);
+		dev->devname = NULL;
+	}
+
+	if (dev->unique) {
+		drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER);
+		dev->unique = NULL;
+		dev->unique_len = 0;
+	}
+				/* Clear pid list */
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		for (pt = dev->magiclist[i].head; pt; pt = next) {
+			next = pt->next;
+			drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
+		}
+		dev->magiclist[i].head = dev->magiclist[i].tail = NULL;
+	}
+   				/* Clear AGP information */
+	if (dev->agp) {
+		drm_agp_mem_t *entry;
+		drm_agp_mem_t *nexte;
+
+				/* Remove AGP resources, but leave dev->agp
+                                   intact until r128_cleanup is called. */
+		for (entry = dev->agp->memory; entry; entry = nexte) {
+			nexte = entry->next;
+			if (entry->bound) drm_unbind_agp(entry->memory);
+			drm_free_agp(entry->memory, entry->pages);
+			drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS);
+		}
+		dev->agp->memory = NULL;
+
+		if (dev->agp->acquired) _drm_agp_release();
+
+		dev->agp->acquired = 0;
+		dev->agp->enabled  = 0;
+	}
+				/* Clear vma list (only built for debugging) */
+	if (dev->vmalist) {
+		for (vma = dev->vmalist; vma; vma = vma_next) {
+			vma_next = vma->next;
+			drm_free(vma, sizeof(*vma), DRM_MEM_VMAS);
+		}
+		dev->vmalist = NULL;
+	}
+
+				/* Clear map area and mtrr information */
+	if (dev->maplist) {
+		for (i = 0; i < dev->map_count; i++) {
+			map = dev->maplist[i];
+			switch (map->type) {
+			case _DRM_REGISTERS:
+			case _DRM_FRAME_BUFFER:
+#ifdef CONFIG_MTRR
+				if (map->mtrr >= 0) {
+					int retcode;
+					retcode = mtrr_del(map->mtrr,
+							   map->offset,
+							   map->size);
+					DRM_DEBUG("mtrr_del = %d\n", retcode);
+				}
+#endif
+				drm_ioremapfree(map->handle, map->size);
+				break;
+			case _DRM_SHM:
+				drm_free_pages((unsigned long)map->handle,
+					       drm_order(map->size)
+					       - PAGE_SHIFT,
+					       DRM_MEM_SAREA);
+				break;
+			case _DRM_AGP:
+				break;
+			}
+			drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+		}
+		drm_free(dev->maplist,
+			 dev->map_count * sizeof(*dev->maplist),
+			 DRM_MEM_MAPS);
+		dev->maplist   = NULL;
+		dev->map_count = 0;
+	}
+
+	if (dev->queuelist) {
+		for (i = 0; i < dev->queue_count; i++) {
+			drm_waitlist_destroy(&dev->queuelist[i]->waitlist);
+			if (dev->queuelist[i]) {
+				drm_free(dev->queuelist[i],
+					 sizeof(*dev->queuelist[0]),
+					 DRM_MEM_QUEUES);
+				dev->queuelist[i] = NULL;
+			}
+		}
+		drm_free(dev->queuelist,
+			 dev->queue_slots * sizeof(*dev->queuelist),
+			 DRM_MEM_QUEUES);
+		dev->queuelist	 = NULL;
+	}
+
+	drm_dma_takedown(dev);
+
+	dev->queue_count     = 0;
+	if (dev->lock.hw_lock) {
+		dev->lock.hw_lock    = NULL; /* SHM removed */
+		dev->lock.pid	     = 0;
+		wake_up_interruptible(&dev->lock.lock_queue);
+	}
+	up(&dev->struct_sem);
+
+	return 0;
+}
+
+/* i810_init is called via init_module at module load time, or via
+ * linux/init/main.c (this is not currently supported). */
+
+static int __init i810_init(void)
+{
+	int		      retcode;
+	drm_device_t	      *dev = &i810_device;
+
+	DRM_DEBUG("\n");
+
+	memset((void *)dev, 0, sizeof(*dev));
+	dev->count_lock	  = SPIN_LOCK_UNLOCKED;
+	sema_init(&dev->struct_sem, 1);
+
+#ifdef MODULE
+	drm_parse_options(i810);
+#endif
+	DRM_DEBUG("doing misc_register\n");
+	if ((retcode = misc_register(&i810_misc))) {
+		DRM_ERROR("Cannot register \"%s\"\n", I810_NAME);
+		return retcode;
+	}
+	dev->device = MKDEV(MISC_MAJOR, i810_misc.minor);
+	dev->name   = I810_NAME;
+
+   	DRM_DEBUG("doing mem init\n");
+	drm_mem_init();
+	DRM_DEBUG("doing proc init\n");
+	drm_proc_init(dev);
+	DRM_DEBUG("doing agp init\n");
+	dev->agp    = drm_agp_init();
+   	if(dev->agp == NULL) {
+	   	DRM_INFO("The i810 drm module requires the agpgart module"
+			 " to function correctly\nPlease load the agpgart"
+			 " module before you load the i810 module\n");
+	   	drm_proc_cleanup();
+	   	misc_deregister(&i810_misc);
+	   	i810_takedown(dev);
+	   	return -ENOMEM;
+	}
+	DRM_DEBUG("doing ctxbitmap init\n");
+	if((retcode = drm_ctxbitmap_init(dev))) {
+		DRM_ERROR("Cannot allocate memory for context bitmap.\n");
+		drm_proc_cleanup();
+		misc_deregister(&i810_misc);
+		i810_takedown(dev);
+		return retcode;
+	}
+
+	DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
+		 I810_NAME,
+		 I810_MAJOR,
+		 I810_MINOR,
+		 I810_PATCHLEVEL,
+		 I810_DATE,
+		 i810_misc.minor);
+
+	return 0;
+}
+
+/* i810_cleanup is called via cleanup_module at module unload time. */
+
+static void __exit i810_cleanup(void)
+{
+	drm_device_t	      *dev = &i810_device;
+
+	DRM_DEBUG("\n");
+
+	drm_proc_cleanup();
+	if (misc_deregister(&i810_misc)) {
+		DRM_ERROR("Cannot unload module\n");
+	} else {
+		DRM_INFO("Module unloaded\n");
+	}
+	drm_ctxbitmap_cleanup(dev);
+	i810_takedown(dev);
+	if (dev->agp) {
+		drm_agp_uninit();
+		drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS);
+		dev->agp = NULL;
+	}
+}
+
+module_init(i810_init);
+module_exit(i810_cleanup);
+
+
+int i810_version(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_version_t version;
+	int	      len;
+
+	if (copy_from_user(&version,
+			   (drm_version_t *)arg,
+			   sizeof(version)))
+		return -EFAULT;
+
+#define DRM_COPY(name,value)				     \
+	len = strlen(value);				     \
+	if (len > name##_len) len = name##_len;		     \
+	name##_len = strlen(value);			     \
+	if (len && name) {				     \
+		if (copy_to_user(name, value, len))          \
+			return -EFAULT;			     \
+	}
+
+	version.version_major	   = I810_MAJOR;
+	version.version_minor	   = I810_MINOR;
+	version.version_patchlevel = I810_PATCHLEVEL;
+
+	DRM_COPY(version.name, I810_NAME);
+	DRM_COPY(version.date, I810_DATE);
+	DRM_COPY(version.desc, I810_DESC);
+
+	if (copy_to_user((drm_version_t *)arg,
+			 &version,
+			 sizeof(version)))
+		return -EFAULT;
+	return 0;
+}
+
+int i810_open(struct inode *inode, struct file *filp)
+{
+	drm_device_t  *dev    = &i810_device;
+	int	      retcode = 0;
+
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	if (!(retcode = drm_open_helper(inode, filp, dev))) {
+#if LINUX_VERSION_CODE < 0x020333
+		MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+		atomic_inc(&dev->total_open);
+		spin_lock(&dev->count_lock);
+		if (!dev->open_count++) {
+			spin_unlock(&dev->count_lock);
+			return i810_setup(dev);
+		}
+		spin_unlock(&dev->count_lock);
+	}
+	return retcode;
+}
+
+int i810_release(struct inode *inode, struct file *filp)
+{
+	drm_file_t    *priv   = filp->private_data;
+	drm_device_t  *dev;
+	int	      retcode = 0;
+
+	lock_kernel();
+	dev    = priv->dev;
+	DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n",
+		  current->pid, dev->device, dev->open_count);
+
+	if (dev->lock.hw_lock && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)
+	    && dev->lock.pid == current->pid) {
+	      	i810_reclaim_buffers(dev, priv->pid);
+		DRM_ERROR("Process %d dead, freeing lock for context %d\n",
+			  current->pid,
+			  _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+		drm_lock_free(dev,
+			      &dev->lock.hw_lock->lock,
+			      _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+
+				/* FIXME: may require heavy-handed reset of
+                                   hardware at this point, possibly
+                                   processed via a callback to the X
+                                   server. */
+	} else if (dev->lock.hw_lock) {
+	   	/* The lock is required to reclaim buffers */
+	   	DECLARE_WAITQUEUE(entry, current);
+	   	add_wait_queue(&dev->lock.lock_queue, &entry);
+		for (;;) {
+			current->state = TASK_INTERRUPTIBLE;
+			if (!dev->lock.hw_lock) {
+				/* Device has been unregistered */
+				retcode = -EINTR;
+				break;
+			}
+			if (drm_lock_take(&dev->lock.hw_lock->lock,
+					  DRM_KERNEL_CONTEXT)) {
+				dev->lock.pid	    = priv->pid;
+				dev->lock.lock_time = jiffies;
+				atomic_inc(&dev->total_locks);
+				break;	/* Got lock */
+			}
+				/* Contention */
+			atomic_inc(&dev->total_sleeps);
+			schedule();
+			if (signal_pending(current)) {
+				retcode = -ERESTARTSYS;
+				break;
+			}
+		}
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&dev->lock.lock_queue, &entry);
+	   	if(!retcode) {
+		   	i810_reclaim_buffers(dev, priv->pid);
+		   	drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				      DRM_KERNEL_CONTEXT);
+		}
+	}
+	drm_fasync(-1, filp, 0);
+
+	down(&dev->struct_sem);
+	if (priv->prev) priv->prev->next = priv->next;
+	else		dev->file_first	 = priv->next;
+	if (priv->next) priv->next->prev = priv->prev;
+	else		dev->file_last	 = priv->prev;
+	up(&dev->struct_sem);
+
+	drm_free(priv, sizeof(*priv), DRM_MEM_FILES);
+#if LINUX_VERSION_CODE < 0x020333
+	MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+   	atomic_inc(&dev->total_close);
+   	spin_lock(&dev->count_lock);
+   	if (!--dev->open_count) {
+	   	if (atomic_read(&dev->ioctl_count) || dev->blocked) {
+		   	DRM_ERROR("Device busy: %d %d\n",
+				  atomic_read(&dev->ioctl_count),
+				  dev->blocked);
+		   	spin_unlock(&dev->count_lock);
+			unlock_kernel();
+		   	return -EBUSY;
+		}
+	   	spin_unlock(&dev->count_lock);
+		unlock_kernel();
+		return i810_takedown(dev);
+	}
+	spin_unlock(&dev->count_lock);
+	unlock_kernel();
+	return retcode;
+}
+
+/* drm_ioctl is called whenever a process performs an ioctl on /dev/drm. */
+
+int i810_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	int		 nr	 = DRM_IOCTL_NR(cmd);
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	int		 retcode = 0;
+	drm_ioctl_desc_t *ioctl;
+	drm_ioctl_t	 *func;
+
+	atomic_inc(&dev->ioctl_count);
+	atomic_inc(&dev->total_ioctl);
+	++priv->ioctl_count;
+
+	DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n",
+		  current->pid, cmd, nr, dev->device, priv->authenticated);
+
+	if (nr >= I810_IOCTL_COUNT) {
+		retcode = -EINVAL;
+	} else {
+		ioctl	  = &i810_ioctls[nr];
+		func	  = ioctl->func;
+
+		if (!func) {
+			DRM_DEBUG("no function\n");
+			retcode = -EINVAL;
+		} else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN))
+			    || (ioctl->auth_needed && !priv->authenticated)) {
+			retcode = -EACCES;
+		} else {
+			retcode = (func)(inode, filp, cmd, arg);
+		}
+	}
+
+	atomic_dec(&dev->ioctl_count);
+	return retcode;
+}
+
+int i810_unlock(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	drm_lock_t	  lock;
+
+	if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)))
+		return -EFAULT;
+
+	if (lock.context == DRM_KERNEL_CONTEXT) {
+		DRM_ERROR("Process %d using kernel context %d\n",
+			  current->pid, lock.context);
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("%d frees lock (%d holds)\n",
+		  lock.context,
+		  _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+	atomic_inc(&dev->total_unlocks);
+	if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock))
+		atomic_inc(&dev->total_contends);
+   	drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT);
+	if (!dev->context_flag) {
+		if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				  DRM_KERNEL_CONTEXT)) {
+			DRM_ERROR("\n");
+		}
+	}
+#if DRM_DMA_HISTOGRAM
+	atomic_inc(&dev->histo.lhld[drm_histogram_slot(get_cycles()
+						       - dev->lck_start)]);
+#endif
+
+	unblock_all_signals();
+	return 0;
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/i810_drv.h linux/drivers/char/drm-4.0/i810_drv.h
--- linux-2.4.18-pre2/drivers/char/drm-4.0/i810_drv.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/i810_drv.h	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,225 @@
+/* i810_drv.h -- Private header for the Matrox g200/g400 driver -*- linux-c -*-
+ * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
+ * 	    Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#ifndef _I810_DRV_H_
+#define _I810_DRV_H_
+
+typedef struct drm_i810_buf_priv {
+   	u32 *in_use;
+   	int my_use_idx;
+	int currently_mapped;
+	void *virtual;
+	void *kernel_virtual;
+	int map_count;
+   	struct vm_area_struct *vma;
+} drm_i810_buf_priv_t;
+
+typedef struct _drm_i810_ring_buffer{
+	int tail_mask;
+	unsigned long Start;
+	unsigned long End;
+	unsigned long Size;
+	u8 *virtual_start;
+	int head;
+	int tail;
+	int space;
+} drm_i810_ring_buffer_t;
+
+typedef struct drm_i810_private {
+   	int ring_map_idx;
+   	int buffer_map_idx;
+
+   	drm_i810_ring_buffer_t ring;
+	drm_i810_sarea_t *sarea_priv;
+
+      	unsigned long hw_status_page;
+   	unsigned long counter;
+
+   	atomic_t flush_done;
+   	wait_queue_head_t flush_queue;	/* Processes waiting until flush    */
+	drm_buf_t *mmap_buffer;
+
+	
+	u32 front_di1, back_di1, zi1;
+	
+	int back_offset;
+	int depth_offset;
+	int w, h;
+	int pitch;
+} drm_i810_private_t;
+
+				/* i810_drv.c */
+extern int  i810_version(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  i810_open(struct inode *inode, struct file *filp);
+extern int  i810_release(struct inode *inode, struct file *filp);
+extern int  i810_ioctl(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  i810_unlock(struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg);
+
+				/* i810_dma.c */
+extern int  i810_dma_schedule(drm_device_t *dev, int locked);
+extern int  i810_getbuf(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  i810_irq_install(drm_device_t *dev, int irq);
+extern int  i810_irq_uninstall(drm_device_t *dev);
+extern int  i810_control(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  i810_lock(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  i810_dma_init(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  i810_flush_ioctl(struct inode *inode, struct file *filp,
+			     unsigned int cmd, unsigned long arg);
+extern void i810_reclaim_buffers(drm_device_t *dev, pid_t pid);
+extern int  i810_getage(struct inode *inode, struct file *filp, unsigned int cmd,
+			unsigned long arg);
+extern int i810_mmap_buffers(struct file *filp, struct vm_area_struct *vma);
+extern int i810_copybuf(struct inode *inode, struct file *filp, 
+			unsigned int cmd, unsigned long arg);
+extern int i810_docopy(struct inode *inode, struct file *filp, 
+		       unsigned int cmd, unsigned long arg);
+
+				/* i810_bufs.c */
+extern int  i810_addbufs(struct inode *inode, struct file *filp, 
+			unsigned int cmd, unsigned long arg);
+extern int  i810_infobufs(struct inode *inode, struct file *filp, 
+			 unsigned int cmd, unsigned long arg);
+extern int  i810_markbufs(struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg);
+extern int  i810_freebufs(struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg);
+extern int  i810_addmap(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+
+				/* i810_context.c */
+extern int  i810_resctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  i810_addctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  i810_modctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  i810_getctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  i810_switchctx(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  i810_newctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  i810_rmctx(struct inode *inode, struct file *filp,
+		      unsigned int cmd, unsigned long arg);
+
+extern int  i810_context_switch(drm_device_t *dev, int old, int new);
+extern int  i810_context_switch_complete(drm_device_t *dev, int new);
+
+#define I810_VERBOSE 0
+
+
+int i810_dma_vertex(struct inode *inode, struct file *filp,
+		    unsigned int cmd, unsigned long arg);
+
+int i810_swap_bufs(struct inode *inode, struct file *filp,
+		   unsigned int cmd, unsigned long arg);
+
+int i810_clear_bufs(struct inode *inode, struct file *filp,
+		    unsigned int cmd, unsigned long arg);
+
+#define GFX_OP_USER_INTERRUPT 		((0<<29)|(2<<23))
+#define GFX_OP_BREAKPOINT_INTERRUPT	((0<<29)|(1<<23))
+#define CMD_REPORT_HEAD			(7<<23)
+#define CMD_STORE_DWORD_IDX		((0x21<<23) | 0x1)
+#define CMD_OP_BATCH_BUFFER  ((0x0<<29)|(0x30<<23)|0x1)
+
+#define INST_PARSER_CLIENT   0x00000000
+#define INST_OP_FLUSH        0x02000000
+#define INST_FLUSH_MAP_CACHE 0x00000001
+
+
+#define BB1_START_ADDR_MASK   (~0x7)
+#define BB1_PROTECTED         (1<<0)
+#define BB1_UNPROTECTED       (0<<0)
+#define BB2_END_ADDR_MASK     (~0x7)
+
+#define I810REG_HWSTAM		0x02098
+#define I810REG_INT_IDENTITY_R	0x020a4
+#define I810REG_INT_MASK_R 	0x020a8
+#define I810REG_INT_ENABLE_R	0x020a0
+
+#define LP_RING     		0x2030
+#define HP_RING     		0x2040
+#define RING_TAIL      		0x00
+#define TAIL_ADDR		0x000FFFF8
+#define RING_HEAD      		0x04
+#define HEAD_WRAP_COUNT     	0xFFE00000
+#define HEAD_WRAP_ONE       	0x00200000
+#define HEAD_ADDR           	0x001FFFFC
+#define RING_START     		0x08
+#define START_ADDR          	0x00FFFFF8
+#define RING_LEN       		0x0C
+#define RING_NR_PAGES       	0x000FF000 
+#define RING_REPORT_MASK    	0x00000006
+#define RING_REPORT_64K     	0x00000002
+#define RING_REPORT_128K    	0x00000004
+#define RING_NO_REPORT      	0x00000000
+#define RING_VALID_MASK     	0x00000001
+#define RING_VALID          	0x00000001
+#define RING_INVALID        	0x00000000
+
+#define GFX_OP_SCISSOR         ((0x3<<29)|(0x1c<<24)|(0x10<<19))
+#define SC_UPDATE_SCISSOR       (0x1<<1)
+#define SC_ENABLE_MASK          (0x1<<0)
+#define SC_ENABLE               (0x1<<0)
+
+#define GFX_OP_SCISSOR_INFO    ((0x3<<29)|(0x1d<<24)|(0x81<<16)|(0x1))
+#define SCI_YMIN_MASK      (0xffff<<16)
+#define SCI_XMIN_MASK      (0xffff<<0)
+#define SCI_YMAX_MASK      (0xffff<<16)
+#define SCI_XMAX_MASK      (0xffff<<0)
+
+#define GFX_OP_COLOR_FACTOR      ((0x3<<29)|(0x1d<<24)|(0x1<<16)|0x0)
+#define GFX_OP_STIPPLE           ((0x3<<29)|(0x1d<<24)|(0x83<<16))
+#define GFX_OP_MAP_INFO          ((0x3<<29)|(0x1d<<24)|0x2)
+#define GFX_OP_DESTBUFFER_VARS   ((0x3<<29)|(0x1d<<24)|(0x85<<16)|0x0)
+#define GFX_OP_DRAWRECT_INFO     ((0x3<<29)|(0x1d<<24)|(0x80<<16)|(0x3))
+#define GFX_OP_PRIMITIVE         ((0x3<<29)|(0x1f<<24))
+
+#define CMD_OP_Z_BUFFER_INFO     ((0x0<<29)|(0x16<<23))
+#define CMD_OP_DESTBUFFER_INFO   ((0x0<<29)|(0x15<<23))
+
+#define BR00_BITBLT_CLIENT   0x40000000
+#define BR00_OP_COLOR_BLT    0x10000000
+#define BR00_OP_SRC_COPY_BLT 0x10C00000
+#define BR13_SOLID_PATTERN   0x80000000
+
+
+
+#endif
+
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/init.c linux/drivers/char/drm-4.0/init.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/init.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/init.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,113 @@
+/* init.c -- Setup/Cleanup for DRM -*- linux-c -*-
+ * Created: Mon Jan  4 08:58:31 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+int			      drm_flags		= 0;
+
+/* drm_parse_option parses a single option.  See description for
+   drm_parse_options for details. */
+
+static void drm_parse_option(char *s)
+{
+	char *c, *r;
+	
+	DRM_DEBUG("\"%s\"\n", s);
+	if (!s || !*s) return;
+	for (c = s; *c && *c != ':'; c++); /* find : or \0 */
+	if (*c) r = c + 1; else r = NULL;  /* remember remainder */
+	*c = '\0';			   /* terminate */
+	if (!strcmp(s, "noctx")) {
+		drm_flags |= DRM_FLAG_NOCTX;
+		DRM_INFO("Server-mediated context switching OFF\n");
+		return;
+	}
+	if (!strcmp(s, "debug")) {
+		drm_flags |= DRM_FLAG_DEBUG;
+		DRM_INFO("Debug messages ON\n");
+		return;
+	}
+	DRM_ERROR("\"%s\" is not a valid option\n", s);
+	return;
+}
+
+/* drm_parse_options parse the insmod "drm=" options, or the command-line
+ * options passed to the kernel via LILO.  The grammar of the format is as
+ * follows:
+ *
+ * drm		::= 'drm=' option_list
+ * option_list	::= option [ ';' option_list ]
+ * option	::= 'device:' major
+ *		|   'debug' 
+ *		|   'noctx'
+ * major	::= INTEGER
+ *
+ * Note that 's' contains option_list without the 'drm=' part.
+ *
+ * device=major,minor specifies the device number used for /dev/drm
+ *	  if major == 0 then the misc device is used
+ *	  if major == 0 and minor == 0 then dynamic misc allocation is used
+ * debug=on specifies that debugging messages will be printk'd
+ * debug=trace specifies that each function call will be logged via printk
+ * debug=off turns off all debugging options
+ *
+ */
+
+void drm_parse_options(char *s)
+{
+	char *h, *t, *n;
+	
+	DRM_DEBUG("\"%s\"\n", s ?: "");
+	if (!s || !*s) return;
+
+	for (h = t = n = s; h && *h; h = n) {
+		for (; *t && *t != ';'; t++);	       /* find ; or \0 */
+		if (*t) n = t + 1; else n = NULL;      /* remember next */
+		*t = '\0';			       /* terminate */
+		drm_parse_option(h);		       /* parse */
+	}
+}
+
+/* drm_cpu_valid returns non-zero if the DRI will run on this CPU, and 0
+ * otherwise. */
+
+int drm_cpu_valid(void)
+{
+#if defined(__i386__)
+	if (boot_cpu_data.x86 == 3) return 0; /* No cmpxchg on a 386 */
+#endif
+#if defined(__sparc__) && !defined(__sparc_v9__)
+	if (1)
+		return 0; /* No cmpxchg before v9 sparc. */
+#endif
+	return 1;
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/ioctl.c linux/drivers/char/drm-4.0/ioctl.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/ioctl.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/ioctl.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,99 @@
+/* ioctl.c -- IOCTL processing for DRM -*- linux-c -*-
+ * Created: Fri Jan  8 09:01:26 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+int drm_irq_busid(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_irq_busid_t p;
+	struct pci_dev	*dev;
+
+	if (copy_from_user(&p, (drm_irq_busid_t *)arg, sizeof(p)))
+		return -EFAULT;
+	dev = pci_find_slot(p.busnum, PCI_DEVFN(p.devnum, p.funcnum));
+	if (dev) p.irq = dev->irq;
+	else	 p.irq = 0;
+	DRM_DEBUG("%d:%d:%d => IRQ %d\n",
+		  p.busnum, p.devnum, p.funcnum, p.irq);
+	if (copy_to_user((drm_irq_busid_t *)arg, &p, sizeof(p)))
+		return -EFAULT;
+	return 0;
+}
+
+int drm_getunique(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_unique_t	 u;
+
+	if (copy_from_user(&u, (drm_unique_t *)arg, sizeof(u)))
+		return -EFAULT;
+	if (u.unique_len >= dev->unique_len) {
+		if (copy_to_user(u.unique, dev->unique, dev->unique_len))
+			return -EFAULT;
+	}
+	u.unique_len = dev->unique_len;
+	if (copy_to_user((drm_unique_t *)arg, &u, sizeof(u)))
+		return -EFAULT;
+	return 0;
+}
+
+int drm_setunique(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_unique_t	 u;
+
+	if (dev->unique_len || dev->unique)
+		return -EBUSY;
+
+	if (copy_from_user(&u, (drm_unique_t *)arg, sizeof(u)))
+		return -EFAULT;
+
+	if (!u.unique_len || u.unique_len > 1024)
+		return -EINVAL;
+	
+	dev->unique_len = u.unique_len;
+	dev->unique	= drm_alloc(u.unique_len + 1, DRM_MEM_DRIVER);
+	if (copy_from_user(dev->unique, u.unique, dev->unique_len))
+		return -EFAULT;
+	dev->unique[dev->unique_len] = '\0';
+
+	dev->devname = drm_alloc(strlen(dev->name) + strlen(dev->unique) + 2,
+				 DRM_MEM_DRIVER);
+	sprintf(dev->devname, "%s@%s", dev->name, dev->unique);
+
+	return 0;
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/lists.c linux/drivers/char/drm-4.0/lists.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/lists.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/lists.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,218 @@
+/* lists.c -- Buffer list handling routines -*- linux-c -*-
+ * Created: Mon Apr 19 20:54:22 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+int drm_waitlist_create(drm_waitlist_t *bl, int count)
+{
+	if (bl->count) return -EINVAL;
+	
+	bl->count      = count;
+	bl->bufs       = drm_alloc((bl->count + 2) * sizeof(*bl->bufs),
+				   DRM_MEM_BUFLISTS);
+	bl->rp	       = bl->bufs;
+	bl->wp	       = bl->bufs;
+	bl->end	       = &bl->bufs[bl->count+1];
+	bl->write_lock = SPIN_LOCK_UNLOCKED;
+	bl->read_lock  = SPIN_LOCK_UNLOCKED;
+	return 0;
+}
+
+int drm_waitlist_destroy(drm_waitlist_t *bl)
+{
+	if (bl->rp != bl->wp) return -EINVAL;
+	if (bl->bufs) drm_free(bl->bufs,
+			       (bl->count + 2) * sizeof(*bl->bufs),
+			       DRM_MEM_BUFLISTS);
+	bl->count = 0;
+	bl->bufs  = NULL;
+	bl->rp	  = NULL;
+	bl->wp	  = NULL;
+	bl->end	  = NULL;
+	return 0;
+}
+
+int drm_waitlist_put(drm_waitlist_t *bl, drm_buf_t *buf)
+{							
+	int	      left;
+	unsigned long flags;
+
+	left = DRM_LEFTCOUNT(bl);
+	if (!left) {
+		DRM_ERROR("Overflow while adding buffer %d from pid %d\n",
+			  buf->idx, buf->pid);
+		return -EINVAL;
+	}
+#if DRM_DMA_HISTOGRAM
+	buf->time_queued = get_cycles();
+#endif
+	buf->list	 = DRM_LIST_WAIT;
+	
+	spin_lock_irqsave(&bl->write_lock, flags);
+	*bl->wp = buf;
+	if (++bl->wp >= bl->end) bl->wp = bl->bufs;
+	spin_unlock_irqrestore(&bl->write_lock, flags);
+	
+	return 0;
+}
+
+drm_buf_t *drm_waitlist_get(drm_waitlist_t *bl)
+{
+	drm_buf_t     *buf;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bl->read_lock, flags);
+	buf = *bl->rp;
+	if (bl->rp == bl->wp) {
+		spin_unlock_irqrestore(&bl->read_lock, flags);
+		return NULL;
+	}				     
+	if (++bl->rp >= bl->end) bl->rp = bl->bufs;
+	spin_unlock_irqrestore(&bl->read_lock, flags);
+	
+	return buf;
+}
+
+int drm_freelist_create(drm_freelist_t *bl, int count)
+{
+	atomic_set(&bl->count, 0);
+	bl->next      = NULL;
+	init_waitqueue_head(&bl->waiting);
+	bl->low_mark  = 0;
+	bl->high_mark = 0;
+	atomic_set(&bl->wfh,   0);
+	bl->lock      = SPIN_LOCK_UNLOCKED;
+	++bl->initialized;
+	return 0;
+}
+
+int drm_freelist_destroy(drm_freelist_t *bl)
+{
+	atomic_set(&bl->count, 0);
+	bl->next = NULL;
+	return 0;
+}
+
+int drm_freelist_put(drm_device_t *dev, drm_freelist_t *bl, drm_buf_t *buf)
+{
+	drm_device_dma_t *dma  = dev->dma;
+
+	if (!dma) {
+		DRM_ERROR("No DMA support\n");
+		return 1;
+	}
+
+	if (buf->waiting || buf->pending || buf->list == DRM_LIST_FREE) {
+		DRM_ERROR("Freed buffer %d: w%d, p%d, l%d\n",
+			  buf->idx, buf->waiting, buf->pending, buf->list);
+	}
+	if (!bl) return 1;
+#if DRM_DMA_HISTOGRAM
+	buf->time_freed = get_cycles();
+	drm_histogram_compute(dev, buf);
+#endif
+	buf->list	= DRM_LIST_FREE;
+	
+	spin_lock(&bl->lock);
+	buf->next	= bl->next;
+	bl->next	= buf;
+	spin_unlock(&bl->lock);
+	
+	atomic_inc(&bl->count);
+	if (atomic_read(&bl->count) > dma->buf_count) {
+		DRM_ERROR("%d of %d buffers free after addition of %d\n",
+			  atomic_read(&bl->count), dma->buf_count, buf->idx);
+		return 1;
+	}
+				/* Check for high water mark */
+	if (atomic_read(&bl->wfh) && atomic_read(&bl->count)>=bl->high_mark) {
+		atomic_set(&bl->wfh, 0);
+		wake_up_interruptible(&bl->waiting);
+	}
+	return 0;
+}
+
+static drm_buf_t *drm_freelist_try(drm_freelist_t *bl)
+{
+	drm_buf_t	  *buf;
+
+	if (!bl) return NULL;
+	
+				/* Get buffer */
+	spin_lock(&bl->lock);
+	if (!bl->next) {
+		spin_unlock(&bl->lock);
+		return NULL;
+	}
+	buf	  = bl->next;
+	bl->next  = bl->next->next;
+	spin_unlock(&bl->lock);
+	
+	atomic_dec(&bl->count);
+	buf->next = NULL;
+	buf->list = DRM_LIST_NONE;
+	if (buf->waiting || buf->pending) {
+		DRM_ERROR("Free buffer %d: w%d, p%d, l%d\n",
+			  buf->idx, buf->waiting, buf->pending, buf->list);
+	}
+	
+	return buf;
+}
+
+drm_buf_t *drm_freelist_get(drm_freelist_t *bl, int block)
+{
+	drm_buf_t	  *buf	= NULL;
+	DECLARE_WAITQUEUE(entry, current);
+
+	if (!bl || !bl->initialized) return NULL;
+	
+				/* Check for low water mark */
+	if (atomic_read(&bl->count) <= bl->low_mark) /* Became low */
+		atomic_set(&bl->wfh, 1);
+	if (atomic_read(&bl->wfh)) {
+		if (block) {
+			add_wait_queue(&bl->waiting, &entry);
+			for (;;) {
+				current->state = TASK_INTERRUPTIBLE;
+				if (!atomic_read(&bl->wfh)
+				    && (buf = drm_freelist_try(bl))) break;
+				schedule();
+				if (signal_pending(current)) break;
+			}
+			current->state = TASK_RUNNING;
+			remove_wait_queue(&bl->waiting, &entry);
+		}
+		return buf;
+	}
+		
+	return drm_freelist_try(bl);
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/lock.c linux/drivers/char/drm-4.0/lock.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/lock.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/lock.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,252 @@
+/* lock.c -- IOCTLs for locking -*- linux-c -*-
+ * Created: Tue Feb  2 08:37:54 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+int drm_block(struct inode *inode, struct file *filp, unsigned int cmd,
+	      unsigned long arg)
+{
+	DRM_DEBUG("\n");
+	return 0;
+}
+
+int drm_unblock(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	DRM_DEBUG("\n");
+	return 0;
+}
+
+int drm_lock_take(__volatile__ unsigned int *lock, unsigned int context)
+{
+	unsigned int old, new, prev;
+
+	do {
+		old = *lock;
+		if (old & _DRM_LOCK_HELD) new = old | _DRM_LOCK_CONT;
+		else			  new = context | _DRM_LOCK_HELD;
+		prev = cmpxchg(lock, old, new);
+	} while (prev != old);
+	if (_DRM_LOCKING_CONTEXT(old) == context) {
+		if (old & _DRM_LOCK_HELD) {
+			if (context != DRM_KERNEL_CONTEXT) {
+				DRM_ERROR("%d holds heavyweight lock\n",
+					  context);
+			}
+			return 0;
+		}
+	}
+	if (new == (context | _DRM_LOCK_HELD)) {
+				/* Have lock */
+		return 1;
+	}
+	return 0;
+}
+
+/* This takes a lock forcibly and hands it to context.	Should ONLY be used
+   inside *_unlock to give lock to kernel before calling *_dma_schedule. */
+int drm_lock_transfer(drm_device_t *dev,
+		      __volatile__ unsigned int *lock, unsigned int context)
+{
+	unsigned int old, new, prev;
+
+	dev->lock.pid = 0;
+	do {
+		old  = *lock;
+		new  = context | _DRM_LOCK_HELD;
+		prev = cmpxchg(lock, old, new);
+	} while (prev != old);
+	return 1;
+}
+
+int drm_lock_free(drm_device_t *dev,
+		  __volatile__ unsigned int *lock, unsigned int context)
+{
+	unsigned int old, new, prev;
+	pid_t        pid = dev->lock.pid;
+
+	dev->lock.pid = 0;
+	do {
+		old  = *lock;
+		new  = 0;
+		prev = cmpxchg(lock, old, new);
+	} while (prev != old);
+	if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != context) {
+		DRM_ERROR("%d freed heavyweight lock held by %d (pid %d)\n",
+			  context,
+			  _DRM_LOCKING_CONTEXT(old),
+			  pid);
+		return 1;
+	}
+	wake_up_interruptible(&dev->lock.lock_queue);
+	return 0;
+}
+
+static int drm_flush_queue(drm_device_t *dev, int context)
+{
+	DECLARE_WAITQUEUE(entry, current);
+	int		  ret	= 0;
+	drm_queue_t	  *q	= dev->queuelist[context];
+	
+	DRM_DEBUG("\n");
+	
+	atomic_inc(&q->use_count);
+	if (atomic_read(&q->use_count) > 1) {
+		atomic_inc(&q->block_write);
+		add_wait_queue(&q->flush_queue, &entry);
+		atomic_inc(&q->block_count);
+		for (;;) {
+			current->state = TASK_INTERRUPTIBLE;
+			if (!DRM_BUFCOUNT(&q->waitlist)) break;
+			schedule();
+			if (signal_pending(current)) {
+				ret = -EINTR; /* Can't restart */
+				break;
+			}
+		}
+		atomic_dec(&q->block_count);
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&q->flush_queue, &entry);
+	}
+	atomic_dec(&q->use_count);
+	atomic_inc(&q->total_flushed);
+		
+				/* NOTE: block_write is still incremented!
+				   Use drm_flush_unlock_queue to decrement. */
+	return ret;
+}
+
+static int drm_flush_unblock_queue(drm_device_t *dev, int context)
+{
+	drm_queue_t	  *q	= dev->queuelist[context];
+	
+	DRM_DEBUG("\n");
+	
+	atomic_inc(&q->use_count);
+	if (atomic_read(&q->use_count) > 1) {
+		if (atomic_read(&q->block_write)) {
+			atomic_dec(&q->block_write);
+			wake_up_interruptible(&q->write_queue);
+		}
+	}
+	atomic_dec(&q->use_count);
+	return 0;
+}
+
+int drm_flush_block_and_flush(drm_device_t *dev, int context,
+			      drm_lock_flags_t flags)
+{
+	int ret = 0;
+	int i;
+	
+	DRM_DEBUG("\n");
+	
+	if (flags & _DRM_LOCK_FLUSH) {
+		ret = drm_flush_queue(dev, DRM_KERNEL_CONTEXT);
+		if (!ret) ret = drm_flush_queue(dev, context);
+	}
+	if (flags & _DRM_LOCK_FLUSH_ALL) {
+		for (i = 0; !ret && i < dev->queue_count; i++) {
+			ret = drm_flush_queue(dev, i);
+		}
+	}
+	return ret;
+}
+
+int drm_flush_unblock(drm_device_t *dev, int context, drm_lock_flags_t flags)
+{
+	int ret = 0;
+	int i;
+	
+	DRM_DEBUG("\n");
+	
+	if (flags & _DRM_LOCK_FLUSH) {
+		ret = drm_flush_unblock_queue(dev, DRM_KERNEL_CONTEXT);
+		if (!ret) ret = drm_flush_unblock_queue(dev, context);
+	}
+	if (flags & _DRM_LOCK_FLUSH_ALL) {
+		for (i = 0; !ret && i < dev->queue_count; i++) {
+			ret = drm_flush_unblock_queue(dev, i);
+		}
+	}
+		
+	return ret;
+}
+
+int drm_finish(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	int		  ret	  = 0;
+	drm_lock_t	  lock;
+
+	DRM_DEBUG("\n");
+
+	if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)))
+		return -EFAULT;
+	ret = drm_flush_block_and_flush(dev, lock.context, lock.flags);
+	drm_flush_unblock(dev, lock.context, lock.flags);
+	return ret;
+}
+
+/* If we get here, it means that the process has called DRM_IOCTL_LOCK
+   without calling DRM_IOCTL_UNLOCK.
+   
+   If the lock is not held, then let the signal proceed as usual.
+   
+   If the lock is held, then set the contended flag and keep the signal
+   blocked.
+   
+
+   Return 1 if the signal should be delivered normally.
+   Return 0 if the signal should be blocked.  */
+
+int drm_notifier(void *priv)
+{
+	drm_sigdata_t *s = (drm_sigdata_t *)priv;
+	unsigned int  old, new, prev;
+
+
+				/* Allow signal delivery if lock isn't held */
+	if (!_DRM_LOCK_IS_HELD(s->lock->lock)
+	    || _DRM_LOCKING_CONTEXT(s->lock->lock) != s->context) return 1;
+	
+				/* Otherwise, set flag to force call to
+                                   drmUnlock */
+	do {
+		old  = s->lock->lock;
+		new  = old | _DRM_LOCK_CONT;
+		prev = cmpxchg(&s->lock->lock, old, new);
+	} while (prev != old);
+	return 0;
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/memory.c linux/drivers/char/drm-4.0/memory.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/memory.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/memory.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,448 @@
+/* memory.c -- Memory management wrappers for DRM -*- linux-c -*-
+ * Created: Thu Feb  4 14:00:34 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include "drmP.h"
+#include <linux/wrapper.h>
+
+typedef struct drm_mem_stats {
+	const char	  *name;
+	int		  succeed_count;
+	int		  free_count;
+	int		  fail_count;
+	unsigned long	  bytes_allocated;
+	unsigned long	  bytes_freed;
+} drm_mem_stats_t;
+
+static spinlock_t	  drm_mem_lock	    = SPIN_LOCK_UNLOCKED;
+static unsigned long	  drm_ram_available = 0; /* In pages */
+static unsigned long	  drm_ram_used	    = 0;
+static drm_mem_stats_t	  drm_mem_stats[]   = {
+	[DRM_MEM_DMA]	    = { "dmabufs"  },
+	[DRM_MEM_SAREA]	    = { "sareas"   },
+	[DRM_MEM_DRIVER]    = { "driver"   },
+	[DRM_MEM_MAGIC]	    = { "magic"	   },
+	[DRM_MEM_IOCTLS]    = { "ioctltab" },
+	[DRM_MEM_MAPS]	    = { "maplist"  },
+	[DRM_MEM_VMAS]	    = { "vmalist"  },
+	[DRM_MEM_BUFS]	    = { "buflist"  },
+	[DRM_MEM_SEGS]	    = { "seglist"  },
+	[DRM_MEM_PAGES]	    = { "pagelist" },
+	[DRM_MEM_FILES]	    = { "files"	   },
+	[DRM_MEM_QUEUES]    = { "queues"   },
+	[DRM_MEM_CMDS]	    = { "commands" },
+	[DRM_MEM_MAPPINGS]  = { "mappings" },
+	[DRM_MEM_BUFLISTS]  = { "buflists" },
+	[DRM_MEM_AGPLISTS]  = { "agplist"  },
+	[DRM_MEM_TOTALAGP]  = { "totalagp" },
+	[DRM_MEM_BOUNDAGP]  = { "boundagp" },
+	[DRM_MEM_CTXBITMAP] = { "ctxbitmap"},
+	{ NULL, 0, }		/* Last entry must be null */
+};
+
+void drm_mem_init(void)
+{
+	drm_mem_stats_t *mem;
+	struct sysinfo	si;
+	
+	for (mem = drm_mem_stats; mem->name; ++mem) {
+		mem->succeed_count   = 0;
+		mem->free_count	     = 0;
+		mem->fail_count	     = 0;
+		mem->bytes_allocated = 0;
+		mem->bytes_freed     = 0;
+	}
+	
+	si_meminfo(&si);
+#if LINUX_VERSION_CODE < 0x020317
+				/* Changed to page count in 2.3.23 */
+	drm_ram_available = si.totalram >> PAGE_SHIFT;
+#else
+	drm_ram_available = si.totalram;
+#endif
+	drm_ram_used	  = 0;
+}
+
+/* drm_mem_info is called whenever a process reads /dev/drm/mem. */
+
+static int _drm_mem_info(char *buf, char **start, off_t offset, int len,
+			 int *eof, void *data)
+{
+	drm_mem_stats_t *pt;
+
+	if (offset > 0) return 0; /* no partial requests */
+	len  = 0;
+	*eof = 1;
+	DRM_PROC_PRINT("		  total counts			"
+		       " |    outstanding  \n");
+	DRM_PROC_PRINT("type	   alloc freed fail	bytes	   freed"
+		       " | allocs      bytes\n\n");
+	DRM_PROC_PRINT("%-9.9s %5d %5d %4d %10lu kB         |\n",
+		       "system", 0, 0, 0,
+		       drm_ram_available << (PAGE_SHIFT - 10));
+	DRM_PROC_PRINT("%-9.9s %5d %5d %4d %10lu kB         |\n",
+		       "locked", 0, 0, 0, drm_ram_used >> 10);
+	DRM_PROC_PRINT("\n");
+	for (pt = drm_mem_stats; pt->name; pt++) {
+		DRM_PROC_PRINT("%-9.9s %5d %5d %4d %10lu %10lu | %6d %10ld\n",
+			       pt->name,
+			       pt->succeed_count,
+			       pt->free_count,
+			       pt->fail_count,
+			       pt->bytes_allocated,
+			       pt->bytes_freed,
+			       pt->succeed_count - pt->free_count,
+			       (long)pt->bytes_allocated
+			       - (long)pt->bytes_freed);
+	}
+	
+	return len;
+}
+
+int drm_mem_info(char *buf, char **start, off_t offset, int len,
+		 int *eof, void *data)
+{
+	int ret;
+	
+	spin_lock(&drm_mem_lock);
+	ret = _drm_mem_info(buf, start, offset, len, eof, data);
+	spin_unlock(&drm_mem_lock);
+	return ret;
+}
+
+void *drm_alloc(size_t size, int area)
+{
+	void *pt;
+	
+	if (!size) {
+		DRM_MEM_ERROR(area, "Allocating 0 bytes\n");
+		return NULL;
+	}
+	
+	if (!(pt = kmalloc(size, GFP_KERNEL))) {
+		spin_lock(&drm_mem_lock);
+		++drm_mem_stats[area].fail_count;
+		spin_unlock(&drm_mem_lock);
+		return NULL;
+	}
+	spin_lock(&drm_mem_lock);
+	++drm_mem_stats[area].succeed_count;
+	drm_mem_stats[area].bytes_allocated += size;
+	spin_unlock(&drm_mem_lock);
+	return pt;
+}
+
+void *drm_realloc(void *oldpt, size_t oldsize, size_t size, int area)
+{
+	void *pt;
+	
+	if (!(pt = drm_alloc(size, area))) return NULL;
+	if (oldpt && oldsize) {
+		memcpy(pt, oldpt, oldsize);
+		drm_free(oldpt, oldsize, area);
+	}
+	return pt;
+}
+
+char *drm_strdup(const char *s, int area)
+{
+	char *pt;
+	int	 length = s ? strlen(s) : 0;
+	
+	if (!(pt = drm_alloc(length+1, area))) return NULL;
+	strcpy(pt, s);
+	return pt;
+}
+
+void drm_strfree(const char *s, int area)
+{
+	unsigned int size;
+	
+	if (!s) return;
+	
+	size = 1 + (s ? strlen(s) : 0);
+	drm_free((void *)s, size, area);
+}
+
+void drm_free(void *pt, size_t size, int area)
+{
+	int alloc_count;
+	int free_count;
+	
+	if (!pt) DRM_MEM_ERROR(area, "Attempt to free NULL pointer\n");
+	else	 kfree(pt);
+	spin_lock(&drm_mem_lock);
+	drm_mem_stats[area].bytes_freed += size;
+	free_count  = ++drm_mem_stats[area].free_count;
+	alloc_count =	drm_mem_stats[area].succeed_count;
+	spin_unlock(&drm_mem_lock);
+	if (free_count > alloc_count) {
+		DRM_MEM_ERROR(area, "Excess frees: %d frees, %d allocs\n",
+			      free_count, alloc_count);
+	}
+}
+
+unsigned long drm_alloc_pages(int order, int area)
+{
+	unsigned long address;
+	unsigned long bytes	  = PAGE_SIZE << order;
+	unsigned long addr;
+	unsigned int  sz;
+	
+	spin_lock(&drm_mem_lock);
+	if ((drm_ram_used >> PAGE_SHIFT)
+	    > (DRM_RAM_PERCENT * drm_ram_available) / 100) {
+		spin_unlock(&drm_mem_lock);
+		return 0;
+	}
+	spin_unlock(&drm_mem_lock);
+	
+	address = __get_free_pages(GFP_KERNEL, order);
+	if (!address) {
+		spin_lock(&drm_mem_lock);
+		++drm_mem_stats[area].fail_count;
+		spin_unlock(&drm_mem_lock);
+		return 0;
+	}
+	spin_lock(&drm_mem_lock);
+	++drm_mem_stats[area].succeed_count;
+	drm_mem_stats[area].bytes_allocated += bytes;
+	drm_ram_used		            += bytes;
+	spin_unlock(&drm_mem_lock);
+	
+	
+				/* Zero outside the lock */
+	memset((void *)address, 0, bytes);
+	
+				/* Reserve */
+	for (addr = address, sz = bytes;
+	     sz > 0;
+	     addr += PAGE_SIZE, sz -= PAGE_SIZE) {
+#if LINUX_VERSION_CODE >= 0x020400
+				/* Argument type changed in 2.4.0-test6/pre8 */
+		mem_map_reserve(virt_to_page(addr));
+#else
+		mem_map_reserve(MAP_NR(addr));
+#endif
+	}
+	
+	return address;
+}
+
+void drm_free_pages(unsigned long address, int order, int area)
+{
+	unsigned long bytes = PAGE_SIZE << order;
+	int		  alloc_count;
+	int		  free_count;
+	unsigned long addr;
+	unsigned int  sz;
+	
+	if (!address) {
+		DRM_MEM_ERROR(area, "Attempt to free address 0\n");
+	} else {
+				/* Unreserve */
+		for (addr = address, sz = bytes;
+		     sz > 0;
+		     addr += PAGE_SIZE, sz -= PAGE_SIZE) {
+#if LINUX_VERSION_CODE >= 0x020400
+				/* Argument type changed in 2.4.0-test6/pre8 */
+			mem_map_unreserve(virt_to_page(addr));
+#else
+			mem_map_unreserve(MAP_NR(addr));
+#endif
+		}
+		free_pages(address, order);
+	}
+	
+	spin_lock(&drm_mem_lock);
+	free_count  = ++drm_mem_stats[area].free_count;
+	alloc_count =	drm_mem_stats[area].succeed_count;
+	drm_mem_stats[area].bytes_freed += bytes;
+	drm_ram_used			-= bytes;
+	spin_unlock(&drm_mem_lock);
+	if (free_count > alloc_count) {
+		DRM_MEM_ERROR(area,
+			      "Excess frees: %d frees, %d allocs\n",
+			      free_count, alloc_count);
+	}
+}
+
+void *drm_ioremap(unsigned long offset, unsigned long size)
+{
+	void *pt;
+	
+	if (!size) {
+		DRM_MEM_ERROR(DRM_MEM_MAPPINGS,
+			      "Mapping 0 bytes at 0x%08lx\n", offset);
+		return NULL;
+	}
+	
+	if (!(pt = ioremap(offset, size))) {
+		spin_lock(&drm_mem_lock);
+		++drm_mem_stats[DRM_MEM_MAPPINGS].fail_count;
+		spin_unlock(&drm_mem_lock);
+		return NULL;
+	}
+	spin_lock(&drm_mem_lock);
+	++drm_mem_stats[DRM_MEM_MAPPINGS].succeed_count;
+	drm_mem_stats[DRM_MEM_MAPPINGS].bytes_allocated += size;
+	spin_unlock(&drm_mem_lock);
+	return pt;
+}
+
+void drm_ioremapfree(void *pt, unsigned long size)
+{
+	int alloc_count;
+	int free_count;
+	
+	if (!pt)
+		DRM_MEM_ERROR(DRM_MEM_MAPPINGS,
+			      "Attempt to free NULL pointer\n");
+	else
+		iounmap(pt);
+	
+	spin_lock(&drm_mem_lock);
+	drm_mem_stats[DRM_MEM_MAPPINGS].bytes_freed += size;
+	free_count  = ++drm_mem_stats[DRM_MEM_MAPPINGS].free_count;
+	alloc_count =	drm_mem_stats[DRM_MEM_MAPPINGS].succeed_count;
+	spin_unlock(&drm_mem_lock);
+	if (free_count > alloc_count) {
+		DRM_MEM_ERROR(DRM_MEM_MAPPINGS,
+			      "Excess frees: %d frees, %d allocs\n",
+			      free_count, alloc_count);
+	}
+}
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+agp_memory *drm_alloc_agp(int pages, u32 type)
+{
+	agp_memory *handle;
+
+	if (!pages) {
+		DRM_MEM_ERROR(DRM_MEM_TOTALAGP, "Allocating 0 pages\n");
+		return NULL;
+	}
+	
+	if ((handle = drm_agp_allocate_memory(pages, type))) {
+		spin_lock(&drm_mem_lock);
+		++drm_mem_stats[DRM_MEM_TOTALAGP].succeed_count;
+		drm_mem_stats[DRM_MEM_TOTALAGP].bytes_allocated
+			+= pages << PAGE_SHIFT;
+		spin_unlock(&drm_mem_lock);
+		return handle;
+	}
+	spin_lock(&drm_mem_lock);
+	++drm_mem_stats[DRM_MEM_TOTALAGP].fail_count;
+	spin_unlock(&drm_mem_lock);
+	return NULL;
+}
+
+int drm_free_agp(agp_memory *handle, int pages)
+{
+	int           alloc_count;
+	int           free_count;
+	int           retval = -EINVAL;
+
+	if (!handle) {
+		DRM_MEM_ERROR(DRM_MEM_TOTALAGP,
+			      "Attempt to free NULL AGP handle\n");
+		return retval;;
+	}
+	
+	if (drm_agp_free_memory(handle)) {
+		spin_lock(&drm_mem_lock);
+		free_count  = ++drm_mem_stats[DRM_MEM_TOTALAGP].free_count;
+		alloc_count =   drm_mem_stats[DRM_MEM_TOTALAGP].succeed_count;
+		drm_mem_stats[DRM_MEM_TOTALAGP].bytes_freed
+			+= pages << PAGE_SHIFT;
+		spin_unlock(&drm_mem_lock);
+		if (free_count > alloc_count) {
+			DRM_MEM_ERROR(DRM_MEM_TOTALAGP,
+				      "Excess frees: %d frees, %d allocs\n",
+				      free_count, alloc_count);
+		}
+		return 0;
+	}
+	return retval;
+}
+
+int drm_bind_agp(agp_memory *handle, unsigned int start)
+{
+	int retcode = -EINVAL;
+
+	if (!handle) {
+		DRM_MEM_ERROR(DRM_MEM_BOUNDAGP,
+			      "Attempt to bind NULL AGP handle\n");
+		return retcode;
+	}
+
+	if (!(retcode = drm_agp_bind_memory(handle, start))) {
+		spin_lock(&drm_mem_lock);
+		++drm_mem_stats[DRM_MEM_BOUNDAGP].succeed_count;
+		drm_mem_stats[DRM_MEM_BOUNDAGP].bytes_allocated
+			+= handle->page_count << PAGE_SHIFT;
+		spin_unlock(&drm_mem_lock);
+		return retcode;
+	}
+	spin_lock(&drm_mem_lock);
+	++drm_mem_stats[DRM_MEM_BOUNDAGP].fail_count;
+	spin_unlock(&drm_mem_lock);
+	return retcode;
+}
+
+int drm_unbind_agp(agp_memory *handle)
+{
+	int alloc_count;
+	int free_count;
+	int retcode = -EINVAL;
+	
+	if (!handle) {
+		DRM_MEM_ERROR(DRM_MEM_BOUNDAGP,
+			      "Attempt to unbind NULL AGP handle\n");
+		return retcode;
+	}
+
+	if ((retcode = drm_agp_unbind_memory(handle))) return retcode;
+	spin_lock(&drm_mem_lock);
+	free_count  = ++drm_mem_stats[DRM_MEM_BOUNDAGP].free_count;
+	alloc_count = drm_mem_stats[DRM_MEM_BOUNDAGP].succeed_count;
+	drm_mem_stats[DRM_MEM_BOUNDAGP].bytes_freed
+		+= handle->page_count << PAGE_SHIFT;
+	spin_unlock(&drm_mem_lock);
+	if (free_count > alloc_count) {
+		DRM_MEM_ERROR(DRM_MEM_BOUNDAGP,
+			      "Excess frees: %d frees, %d allocs\n",
+			      free_count, alloc_count);
+	}
+	return retcode;
+}
+#endif
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/mga_bufs.c linux/drivers/char/drm-4.0/mga_bufs.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/mga_bufs.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/mga_bufs.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,629 @@
+/* mga_bufs.c -- IOCTLs to manage buffers -*- linux-c -*-
+ * Created: Thu Jan 6 01:47:26 2000 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
+ *	    Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "mga_drv.h"
+#include "linux/un.h"
+
+
+int mga_addbufs_agp(struct inode *inode, struct file *filp, unsigned int cmd,
+		    unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_device_dma_t *dma = dev->dma;
+	drm_buf_desc_t request;
+	drm_buf_entry_t *entry;
+	drm_buf_t *buf;
+	unsigned long offset;
+	unsigned long agp_offset;
+	int count;
+	int order;
+	int size;
+	int alignment;
+	int page_order;
+	int total;
+	int byte_count;
+	int i;
+
+	if (!dma) return -EINVAL;
+
+	if (copy_from_user(&request,
+			   (drm_buf_desc_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	count = request.count;
+	order = drm_order(request.size);
+	size	= 1 << order;
+	agp_offset = request.agp_start;
+	alignment  = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size):size;
+	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
+	total = PAGE_SIZE << page_order;
+	byte_count = 0;
+
+	DRM_DEBUG("count: %d\n", count);
+	DRM_DEBUG("order: %d\n", order);
+	DRM_DEBUG("size: %d\n", size);
+	DRM_DEBUG("agp_offset: %ld\n", agp_offset);
+	DRM_DEBUG("alignment: %d\n", alignment);
+	DRM_DEBUG("page_order: %d\n", page_order);
+	DRM_DEBUG("total: %d\n", total);
+	DRM_DEBUG("byte_count: %d\n", byte_count);
+
+	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+	if (dev->queue_count) return -EBUSY; /* Not while in use */
+	spin_lock(&dev->count_lock);
+	if (dev->buf_use) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	atomic_inc(&dev->buf_alloc);
+	spin_unlock(&dev->count_lock);
+   
+	down(&dev->struct_sem);
+	entry = &dma->bufs[order];
+	if (entry->buf_count) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM; /* May only call once for each order */
+	}
+
+	/* This isnt neccessarily a good limit, but we have to stop a dumb
+	   32 bit overflow problem below */
+	   
+	if ( count < 0 || count > 4096)
+	{
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -EINVAL;
+	}
+		   
+	entry->buflist = drm_alloc(count * sizeof(*entry->buflist),
+				   DRM_MEM_BUFS);
+	if (!entry->buflist) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM;
+	}
+	memset(entry->buflist, 0, count * sizeof(*entry->buflist));
+   
+	entry->buf_size   = size;
+	entry->page_order = page_order;
+	offset = 0;
+
+   
+	while(entry->buf_count < count) {
+		buf = &entry->buflist[entry->buf_count];
+		buf->idx = dma->buf_count + entry->buf_count;
+		buf->total = alignment;
+		buf->order = order;
+		buf->used = 0;
+
+		buf->offset = offset; /* Hrm */
+		buf->bus_address = dev->agp->base + agp_offset + offset;
+		buf->address = (void *)(agp_offset + offset + dev->agp->base);
+		buf->next = NULL;
+		buf->waiting = 0;
+		buf->pending = 0;
+		init_waitqueue_head(&buf->dma_wait);
+		buf->pid = 0;
+
+		buf->dev_private = drm_alloc(sizeof(drm_mga_buf_priv_t),
+					     DRM_MEM_BUFS);
+		buf->dev_priv_size = sizeof(drm_mga_buf_priv_t);
+
+#if DRM_DMA_HISTOGRAM
+		buf->time_queued = 0;
+		buf->time_dispatched = 0;
+		buf->time_completed = 0;
+		buf->time_freed = 0;
+#endif
+		offset = offset + alignment;
+		entry->buf_count++;
+		byte_count += PAGE_SIZE << page_order;
+	}
+   
+	dma->buflist = drm_realloc(dma->buflist,
+				   dma->buf_count * sizeof(*dma->buflist),
+				   (dma->buf_count + entry->buf_count)
+				   * sizeof(*dma->buflist),
+				   DRM_MEM_BUFS);
+	for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++)
+		dma->buflist[i] = &entry->buflist[i - dma->buf_count];
+   
+	dma->buf_count  += entry->buf_count;
+
+	DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count);
+
+	dma->byte_count += byte_count;
+
+	DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count);
+
+	drm_freelist_create(&entry->freelist, entry->buf_count);
+	for (i = 0; i < entry->buf_count; i++) {
+		drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]);
+	}
+   
+	up(&dev->struct_sem);
+   
+	request.count = entry->buf_count;
+	request.size  = size;
+   
+	if (copy_to_user((drm_buf_desc_t *)arg,
+			 &request,
+			 sizeof(request)))
+		return -EFAULT;
+   
+	atomic_dec(&dev->buf_alloc);
+
+	DRM_DEBUG("count: %d\n", count);
+	DRM_DEBUG("order: %d\n", order);
+	DRM_DEBUG("size: %d\n", size);
+	DRM_DEBUG("agp_offset: %ld\n", agp_offset);
+	DRM_DEBUG("alignment: %d\n", alignment);
+	DRM_DEBUG("page_order: %d\n", page_order);
+	DRM_DEBUG("total: %d\n", total);
+	DRM_DEBUG("byte_count: %d\n", byte_count);
+
+	dma->flags = _DRM_DMA_USE_AGP;
+
+	DRM_DEBUG("dma->flags : %x\n", dma->flags);
+
+	return 0;
+}
+
+int mga_addbufs_pci(struct inode *inode, struct file *filp, unsigned int cmd,
+		    unsigned long arg)
+{
+   	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_desc_t	 request;
+	int		 count;
+	int		 order;
+	int		 size;
+	int		 total;
+	int		 page_order;
+	drm_buf_entry_t	 *entry;
+	unsigned long	 page;
+	drm_buf_t	 *buf;
+	int		 alignment;
+	unsigned long	 offset;
+	int		 i;
+	int		 byte_count;
+	int		 page_count;
+
+	if (!dma) return -EINVAL;
+
+	if (copy_from_user(&request,
+			   (drm_buf_desc_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	count	   = request.count;
+	order	   = drm_order(request.size);
+	size	   = 1 << order;
+	
+	DRM_DEBUG("count = %d, size = %d (%d), order = %d, queue_count = %d\n",
+		  request.count, request.size, size, order, dev->queue_count);
+
+	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+	if (dev->queue_count) return -EBUSY; /* Not while in use */
+
+	alignment  = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size):size;
+	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
+	total	   = PAGE_SIZE << page_order;
+
+	spin_lock(&dev->count_lock);
+	if (dev->buf_use) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	atomic_inc(&dev->buf_alloc);
+	spin_unlock(&dev->count_lock);
+	
+	down(&dev->struct_sem);
+	entry = &dma->bufs[order];
+	if (entry->buf_count) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM;	/* May only call once for each order */
+	}
+	
+	if(count < 0 || count > 4096)
+	{
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -EINVAL;
+	}
+	
+	entry->buflist = drm_alloc(count * sizeof(*entry->buflist),
+				   DRM_MEM_BUFS);
+	if (!entry->buflist) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM;
+	}
+	memset(entry->buflist, 0, count * sizeof(*entry->buflist));
+
+	entry->seglist = drm_alloc(count * sizeof(*entry->seglist),
+				   DRM_MEM_SEGS);
+	if (!entry->seglist) {
+		drm_free(entry->buflist,
+			 count * sizeof(*entry->buflist),
+			 DRM_MEM_BUFS);
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM;
+	}
+	memset(entry->seglist, 0, count * sizeof(*entry->seglist));
+
+	dma->pagelist = drm_realloc(dma->pagelist,
+				    dma->page_count * sizeof(*dma->pagelist),
+				    (dma->page_count + (count << page_order))
+				    * sizeof(*dma->pagelist),
+				    DRM_MEM_PAGES);
+	DRM_DEBUG("pagelist: %d entries\n",
+		  dma->page_count + (count << page_order));
+
+
+	entry->buf_size	  = size;
+	entry->page_order = page_order;
+	byte_count	  = 0;
+	page_count	  = 0;
+	while (entry->buf_count < count) {
+		if (!(page = drm_alloc_pages(page_order, DRM_MEM_DMA))) break;
+		entry->seglist[entry->seg_count++] = page;
+		for (i = 0; i < (1 << page_order); i++) {
+			DRM_DEBUG("page %d @ 0x%08lx\n",
+				  dma->page_count + page_count,
+				  page + PAGE_SIZE * i);
+			dma->pagelist[dma->page_count + page_count++]
+				= page + PAGE_SIZE * i;
+		}
+		for (offset = 0;
+		     offset + size <= total && entry->buf_count < count;
+		     offset += alignment, ++entry->buf_count) {
+			buf	     = &entry->buflist[entry->buf_count];
+			buf->idx     = dma->buf_count + entry->buf_count;
+			buf->total   = alignment;
+			buf->order   = order;
+			buf->used    = 0;
+			buf->offset  = (dma->byte_count + byte_count + offset);
+			buf->address = (void *)(page + offset);
+			buf->next    = NULL;
+			buf->waiting = 0;
+			buf->pending = 0;
+			init_waitqueue_head(&buf->dma_wait);
+			buf->pid     = 0;
+#if DRM_DMA_HISTOGRAM
+			buf->time_queued     = 0;
+			buf->time_dispatched = 0;
+			buf->time_completed  = 0;
+			buf->time_freed	     = 0;
+#endif
+			DRM_DEBUG("buffer %d @ %p\n",
+				  entry->buf_count, buf->address);
+		}
+		byte_count += PAGE_SIZE << page_order;
+	}
+
+	dma->buflist = drm_realloc(dma->buflist,
+				   dma->buf_count * sizeof(*dma->buflist),
+				   (dma->buf_count + entry->buf_count)
+				   * sizeof(*dma->buflist),
+				   DRM_MEM_BUFS);
+	for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++)
+		dma->buflist[i] = &entry->buflist[i - dma->buf_count];
+
+	dma->buf_count	+= entry->buf_count;
+	dma->seg_count	+= entry->seg_count;
+	dma->page_count += entry->seg_count << page_order;
+	dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order);
+	
+	drm_freelist_create(&entry->freelist, entry->buf_count);
+	for (i = 0; i < entry->buf_count; i++) {
+		drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]);
+	}
+	
+	up(&dev->struct_sem);
+
+	request.count = entry->buf_count;
+	request.size  = size;
+
+	if (copy_to_user((drm_buf_desc_t *)arg,
+			 &request,
+			 sizeof(request)))
+		return -EFAULT;
+	
+	atomic_dec(&dev->buf_alloc);
+	return 0;
+}
+
+int mga_addbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_buf_desc_t	 request;
+
+	if (copy_from_user(&request,
+			   (drm_buf_desc_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	if(request.flags & _DRM_AGP_BUFFER)
+		return mga_addbufs_agp(inode, filp, cmd, arg);
+	else
+		return mga_addbufs_pci(inode, filp, cmd, arg);
+}
+
+int mga_infobufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_info_t	 request;
+	int		 i;
+	int		 count;
+
+	if (!dma) return -EINVAL;
+
+	spin_lock(&dev->count_lock);
+	if (atomic_read(&dev->buf_alloc)) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	++dev->buf_use;		/* Can't allocate more after this call */
+	spin_unlock(&dev->count_lock);
+
+	if (copy_from_user(&request,
+			   (drm_buf_info_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) {
+		if (dma->bufs[i].buf_count) ++count;
+	}
+	
+	if (request.count >= count) {
+		for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) {
+			if (dma->bufs[i].buf_count) {
+				if (copy_to_user(&request.list[count].count,
+						 &dma->bufs[i].buf_count,
+						 sizeof(dma->bufs[0]
+							.buf_count)) ||
+				    copy_to_user(&request.list[count].size,
+						 &dma->bufs[i].buf_size,
+						 sizeof(dma->bufs[0].buf_size)) ||
+				    copy_to_user(&request.list[count].low_mark,
+						 &dma->bufs[i]
+						 .freelist.low_mark,
+						 sizeof(dma->bufs[0]
+							.freelist.low_mark)) ||
+				    copy_to_user(&request.list[count]
+						 .high_mark,
+						 &dma->bufs[i]
+						 .freelist.high_mark,
+						 sizeof(dma->bufs[0]
+							.freelist.high_mark)))
+					return -EFAULT;
+				++count;
+			}
+		}
+	}
+	request.count = count;
+
+	if (copy_to_user((drm_buf_info_t *)arg,
+			 &request,
+			 sizeof(request)))
+		return -EFAULT;
+	
+	return 0;
+}
+
+int mga_markbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_desc_t	 request;
+	int		 order;
+	drm_buf_entry_t	 *entry;
+
+	if (!dma) return -EINVAL;
+
+	if (copy_from_user(&request, (drm_buf_desc_t *)arg, sizeof(request)))
+		return -EFAULT;
+
+	order = drm_order(request.size);
+	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+	entry = &dma->bufs[order];
+
+	if (request.low_mark < 0 || request.low_mark > entry->buf_count)
+		return -EINVAL;
+	if (request.high_mark < 0 || request.high_mark > entry->buf_count)
+		return -EINVAL;
+
+	entry->freelist.low_mark  = request.low_mark;
+	entry->freelist.high_mark = request.high_mark;
+	
+	return 0;
+}
+
+int mga_freebufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_free_t	 request;
+	int		 i;
+	int		 idx;
+	drm_buf_t	 *buf;
+
+	if (!dma) return -EINVAL;
+
+	if (copy_from_user(&request,
+			   (drm_buf_free_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	for (i = 0; i < request.count; i++) {
+		if (copy_from_user(&idx,
+				   &request.list[i],
+				   sizeof(idx)))
+			return -EFAULT;
+		if (idx < 0 || idx >= dma->buf_count) {
+			DRM_ERROR("Index %d (of %d max)\n",
+				  idx, dma->buf_count - 1);
+			return -EINVAL;
+		}
+		buf = dma->buflist[idx];
+		if (buf->pid != current->pid) {
+			DRM_ERROR("Process %d freeing buffer owned by %d\n",
+				  current->pid, buf->pid);
+			return -EINVAL;
+		}
+		drm_free_buffer(dev, buf);
+	}
+	
+	return 0;
+}
+
+int mga_mapbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	int		 retcode = 0;
+	const int	 zero	 = 0;
+	unsigned long	 virtual;
+	unsigned long	 address;
+	drm_buf_map_t	 request;
+	int		 i;
+
+	if (!dma) return -EINVAL;
+	
+	spin_lock(&dev->count_lock);
+	if (atomic_read(&dev->buf_alloc)) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	++dev->buf_use;		/* Can't allocate more after this call */
+	spin_unlock(&dev->count_lock);
+
+	if (copy_from_user(&request,
+			   (drm_buf_map_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	if (request.count >= dma->buf_count) {
+		if(dma->flags & _DRM_DMA_USE_AGP) {
+			drm_mga_private_t *dev_priv = dev->dev_private;
+			drm_map_t *map = NULL;
+	 
+			map = dev->maplist[dev_priv->buffer_map_idx];
+			if (!map) {
+				retcode = -EINVAL;
+				goto done;
+			}
+
+			DRM_DEBUG("map->offset : %lx\n", map->offset);
+			DRM_DEBUG("map->size : %lx\n", map->size);
+			DRM_DEBUG("map->type : %d\n", map->type);
+			DRM_DEBUG("map->flags : %x\n", map->flags);
+			DRM_DEBUG("map->handle : %p\n", map->handle);
+			DRM_DEBUG("map->mtrr : %d\n", map->mtrr);
+			down_write(&current->mm->mmap_sem);
+			virtual = do_mmap(filp, 0, map->size, 
+					  PROT_READ|PROT_WRITE,
+					  MAP_SHARED, 
+					  (unsigned long)map->offset);
+			up_write(&current->mm->mmap_sem);
+		} else {
+			down_write(&current->mm->mmap_sem);
+			virtual = do_mmap(filp, 0, dma->byte_count,
+					  PROT_READ|PROT_WRITE, MAP_SHARED, 0);
+			up_write(&current->mm->mmap_sem);
+		}
+		if (virtual > -1024UL) {
+			/* Real error */
+			DRM_DEBUG("mmap error\n");
+			retcode = (signed long)virtual;
+			goto done;
+		}
+		request.virtual = (void *)virtual;
+      
+		for (i = 0; i < dma->buf_count; i++) {
+			if (copy_to_user(&request.list[i].idx,
+					 &dma->buflist[i]->idx,
+					 sizeof(request.list[0].idx))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			if (copy_to_user(&request.list[i].total,
+					 &dma->buflist[i]->total,
+					 sizeof(request.list[0].total))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			if (copy_to_user(&request.list[i].used,
+					 &zero,
+					 sizeof(zero))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			address = virtual + dma->buflist[i]->offset;
+			if (copy_to_user(&request.list[i].address,
+					 &address,
+					 sizeof(address))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+		}
+	}
+ done:
+	request.count = dma->buf_count;
+	DRM_DEBUG("%d buffers, retcode = %d\n", request.count, retcode);
+   
+	if (copy_to_user((drm_buf_map_t *)arg,
+			 &request,
+			 sizeof(request)))
+		return -EFAULT;
+
+	DRM_DEBUG("retcode : %d\n", retcode);
+
+	return retcode;
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/mga_context.c linux/drivers/char/drm-4.0/mga_context.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/mga_context.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/mga_context.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,209 @@
+/* mga_context.c -- IOCTLs for mga contexts -*- linux-c -*-
+ * Created: Mon Dec 13 09:51:35 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Author: Rickard E. (Rik) Faith <faith@valinux.com>
+ *	   Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "mga_drv.h"
+
+static int mga_alloc_queue(drm_device_t *dev)
+{
+   	return drm_ctxbitmap_next(dev);
+}
+
+int mga_context_switch(drm_device_t *dev, int old, int new)
+{
+        char        buf[64];
+
+        atomic_inc(&dev->total_ctx);
+
+        if (test_and_set_bit(0, &dev->context_flag)) {
+                DRM_ERROR("Reentering -- FIXME\n");
+                return -EBUSY;
+        }
+
+#if DRM_DMA_HISTOGRAM
+        dev->ctx_start = get_cycles();
+#endif
+        
+        DRM_DEBUG("Context switch from %d to %d\n", old, new);
+
+        if (new == dev->last_context) {
+                clear_bit(0, &dev->context_flag);
+                return 0;
+        }
+        
+        if (drm_flags & DRM_FLAG_NOCTX) {
+                mga_context_switch_complete(dev, new);
+        } else {
+                sprintf(buf, "C %d %d\n", old, new);
+                drm_write_string(dev, buf);
+        }
+        
+        return 0;
+}
+
+int mga_context_switch_complete(drm_device_t *dev, int new)
+{
+        dev->last_context = new;  /* PRE/POST: This is the _only_ writer. */
+        dev->last_switch  = jiffies;
+        
+        if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+                DRM_ERROR("Lock isn't held after context switch\n");
+        }
+
+				/* If a context switch is ever initiated
+                                   when the kernel holds the lock, release
+                                   that lock here. */
+#if DRM_DMA_HISTOGRAM
+        atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles()
+                                                      - dev->ctx_start)]);
+                   
+#endif
+        clear_bit(0, &dev->context_flag);
+        wake_up(&dev->context_wait);
+        
+        return 0;
+}
+
+int mga_resctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_ctx_res_t	res;
+	drm_ctx_t	ctx;
+	int		i;
+
+	if (copy_from_user(&res, (drm_ctx_res_t *)arg, sizeof(res)))
+		return -EFAULT;
+	if (res.count >= DRM_RESERVED_CONTEXTS) {
+		memset(&ctx, 0, sizeof(ctx));
+		for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
+			ctx.handle = i;
+			if (copy_to_user(&res.contexts[i],
+					 &i,
+					 sizeof(i)))
+				return -EFAULT;
+		}
+	}
+	res.count = DRM_RESERVED_CONTEXTS;
+	if (copy_to_user((drm_ctx_res_t *)arg, &res, sizeof(res)))
+		return -EFAULT;
+	return 0;
+}
+
+int mga_addctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	if ((ctx.handle = mga_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) {
+				/* Skip kernel's context and get a new one. */
+		ctx.handle = mga_alloc_queue(dev);
+	}
+        if (ctx.handle == -1) {
+		return -ENOMEM;
+	}
+	DRM_DEBUG("%d\n", ctx.handle);
+	if (copy_to_user((drm_ctx_t *)arg, &ctx, sizeof(ctx)))
+		return -EFAULT;
+	return 0;
+}
+
+int mga_modctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	unsigned long arg)
+{
+   	/* This does nothing for the mga */
+	return 0;
+}
+
+int mga_getctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	unsigned long arg)
+{
+	drm_ctx_t ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx)))
+		return -EFAULT;
+	/* This is 0, because we don't hanlde any context flags */
+	ctx.flags = 0;
+	if (copy_to_user((drm_ctx_t*)arg, &ctx, sizeof(ctx)))
+		return -EFAULT;
+	return 0;
+}
+
+int mga_switchctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		   unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	return mga_context_switch(dev, dev->last_context, ctx.handle);
+}
+
+int mga_newctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	mga_context_switch_complete(dev, ctx.handle);
+
+	return 0;
+}
+
+int mga_rmctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	      unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	if(ctx.handle == DRM_KERNEL_CONTEXT+1) priv->remove_auth_on_close = 1;
+
+      	if(ctx.handle != DRM_KERNEL_CONTEXT) {
+	   	drm_ctxbitmap_free(dev, ctx.handle);
+	}
+	
+	return 0;
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/mga_dma.c linux/drivers/char/drm-4.0/mga_dma.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/mga_dma.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/mga_dma.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,1051 @@
+/* mga_dma.c -- DMA support for mga g200/g400 -*- linux-c -*-
+ * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
+ *	    Jeff Hartmann <jhartmann@valinux.com>
+ *	    Keith Whitwell <keithw@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "mga_drv.h"
+
+#include <linux/interrupt.h>	/* For task queue support */
+
+#define MGA_REG(reg)		2
+#define MGA_BASE(reg)		((unsigned long) \
+				((drm_device_t *)dev)->maplist[MGA_REG(reg)]->handle)
+#define MGA_ADDR(reg)		(MGA_BASE(reg) + reg)
+#define MGA_DEREF(reg)		*(__volatile__ int *)MGA_ADDR(reg)
+#define MGA_READ(reg)		MGA_DEREF(reg)
+#define MGA_WRITE(reg,val) 	do { MGA_DEREF(reg) = val; } while (0)
+
+#define PDEA_pagpxfer_enable 	     0x2
+
+static int mga_flush_queue(drm_device_t *dev);
+
+static unsigned long mga_alloc_page(drm_device_t *dev)
+{
+	unsigned long address;
+
+	address = __get_free_page(GFP_KERNEL);
+	if(address == 0UL) {
+		return 0;
+	}
+	atomic_inc(&virt_to_page(address)->count);
+	set_bit(PG_reserved, &virt_to_page(address)->flags);
+
+	return address;
+}
+
+static void mga_free_page(drm_device_t *dev, unsigned long page)
+{
+	if(!page) return;
+	atomic_dec(&virt_to_page(page)->count);
+	clear_bit(PG_reserved, &virt_to_page(page)->flags);
+	free_page(page);
+	return;
+}
+
+static void mga_delay(void)
+{
+	return;
+}
+
+/* These are two age tags that will never be sent to
+ * the hardware */
+#define MGA_BUF_USED 	0xffffffff
+#define MGA_BUF_FREE	0
+
+static int mga_freelist_init(drm_device_t *dev)
+{
+      	drm_device_dma_t *dma = dev->dma;
+   	drm_buf_t *buf;
+   	drm_mga_buf_priv_t *buf_priv;
+      	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+   	drm_mga_freelist_t *item;
+   	int i;
+
+   	dev_priv->head = drm_alloc(sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER);
+	if(dev_priv->head == NULL) return -ENOMEM;
+   	memset(dev_priv->head, 0, sizeof(drm_mga_freelist_t));
+   	dev_priv->head->age = MGA_BUF_USED;
+
+   	for (i = 0; i < dma->buf_count; i++) {
+	   	buf = dma->buflist[ i ];
+	        buf_priv = buf->dev_private;
+		item = drm_alloc(sizeof(drm_mga_freelist_t),
+				 DRM_MEM_DRIVER);
+	   	if(item == NULL) return -ENOMEM;
+	   	memset(item, 0, sizeof(drm_mga_freelist_t));
+	  	item->age = MGA_BUF_FREE;
+	   	item->prev = dev_priv->head;
+	   	item->next = dev_priv->head->next;
+	   	if(dev_priv->head->next != NULL)
+			dev_priv->head->next->prev = item;
+	   	if(item->next == NULL) dev_priv->tail = item;
+	   	item->buf = buf;
+	   	buf_priv->my_freelist = item;
+		buf_priv->discard = 0;
+		buf_priv->dispatched = 0;
+	   	dev_priv->head->next = item;
+	}
+
+   	return 0;
+}
+
+static void mga_freelist_cleanup(drm_device_t *dev)
+{
+      	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+   	drm_mga_freelist_t *item;
+   	drm_mga_freelist_t *prev;
+
+   	item = dev_priv->head;
+   	while(item) {
+	   	prev = item;
+	   	item = item->next;
+	   	drm_free(prev, sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER);
+	}
+
+   	dev_priv->head = dev_priv->tail = NULL;
+}
+
+/* Frees dispatch lock */
+static inline void mga_dma_quiescent(drm_device_t *dev)
+{
+	drm_device_dma_t  *dma      = dev->dma;
+	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+   	unsigned long end;
+	int i;
+
+	DRM_DEBUG("dispatch_status = 0x%02lx\n", dev_priv->dispatch_status);
+	end = jiffies + (HZ*3);
+    	while(1) {
+		if(!test_and_set_bit(MGA_IN_DISPATCH,
+				     &dev_priv->dispatch_status)) {
+			break;
+		}
+	   	if((signed)(end - jiffies) <= 0) {
+			DRM_ERROR("irqs: %d wanted %d\n",
+				  atomic_read(&dev->total_irq),
+				  atomic_read(&dma->total_lost));
+			DRM_ERROR("lockup: dispatch_status = 0x%02lx,"
+				  " jiffies = %lu, end = %lu\n",
+				  dev_priv->dispatch_status, jiffies, end);
+			return;
+		}
+		for (i = 0 ; i < 2000 ; i++) mga_delay();
+	}
+	end = jiffies + (HZ*3);
+    	DRM_DEBUG("quiescent status : %x\n", MGA_READ(MGAREG_STATUS));
+    	while((MGA_READ(MGAREG_STATUS) & 0x00030001) != 0x00020000) {
+		if((signed)(end - jiffies) <= 0) {
+			DRM_ERROR("irqs: %d wanted %d\n",
+				  atomic_read(&dev->total_irq),
+				  atomic_read(&dma->total_lost));
+			DRM_ERROR("lockup\n");
+			clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status);
+			return;
+		}
+		for (i = 0 ; i < 2000 ; i++) mga_delay();
+	}
+    	sarea_priv->dirty |= MGA_DMA_FLUSH;
+
+    	clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status);
+	DRM_DEBUG("exit, dispatch_status = 0x%02lx\n",
+		  dev_priv->dispatch_status);
+}
+
+static void mga_reset_freelist(drm_device_t *dev)
+{
+   	drm_device_dma_t  *dma      = dev->dma;
+   	drm_buf_t *buf;
+   	drm_mga_buf_priv_t *buf_priv;
+	int i;
+
+   	for (i = 0; i < dma->buf_count; i++) {
+	   	buf = dma->buflist[ i ];
+	        buf_priv = buf->dev_private;
+		buf_priv->my_freelist->age = MGA_BUF_FREE;
+	}
+}
+
+/* Least recently used :
+ * These operations are not atomic b/c they are protected by the
+ * hardware lock */
+
+drm_buf_t *mga_freelist_get(drm_device_t *dev)
+{
+   	DECLARE_WAITQUEUE(entry, current);
+   	drm_mga_private_t *dev_priv =
+     		(drm_mga_private_t *) dev->dev_private;
+	drm_mga_freelist_t *prev;
+   	drm_mga_freelist_t *next;
+	static int failed = 0;
+	int return_null = 0;
+
+	if(failed >= 1000 && dev_priv->tail->age >= dev_priv->last_prim_age) {
+		DRM_DEBUG("Waiting on freelist,"
+			  " tail->age = %d, last_prim_age= %d\n",
+			  dev_priv->tail->age,
+			  dev_priv->last_prim_age);
+	   	add_wait_queue(&dev_priv->buf_queue, &entry);
+		set_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status);
+	   	for (;;) {
+			current->state = TASK_INTERRUPTIBLE;
+		   	mga_dma_schedule(dev, 0);
+			if(dev_priv->tail->age < dev_priv->last_prim_age)
+				break;
+		   	atomic_inc(&dev->total_sleeps);
+		   	schedule();
+		   	if (signal_pending(current)) {
+				++return_null;
+				break;
+			}
+		}
+		clear_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status);
+		current->state = TASK_RUNNING;
+	   	remove_wait_queue(&dev_priv->buf_queue, &entry);
+		if (return_null) return NULL;
+	}
+
+   	if(dev_priv->tail->age < dev_priv->last_prim_age) {
+		prev = dev_priv->tail->prev;
+	   	next = dev_priv->tail;
+	   	prev->next = NULL;
+	   	next->prev = next->next = NULL;
+	   	dev_priv->tail = prev;
+	   	next->age = MGA_BUF_USED;
+		failed = 0;
+	   	return next->buf;
+	}
+
+	failed++;
+   	return NULL;
+}
+
+int mga_freelist_put(drm_device_t *dev, drm_buf_t *buf)
+{
+      	drm_mga_private_t *dev_priv =
+     		(drm_mga_private_t *) dev->dev_private;
+   	drm_mga_buf_priv_t *buf_priv = buf->dev_private;
+	drm_mga_freelist_t *prev;
+   	drm_mga_freelist_t *head;
+   	drm_mga_freelist_t *next;
+
+   	if(buf_priv->my_freelist->age == MGA_BUF_USED) {
+		/* Discarded buffer, put it on the tail */
+		next = buf_priv->my_freelist;
+		next->age = MGA_BUF_FREE;
+		prev = dev_priv->tail;
+		prev->next = next;
+		next->prev = prev;
+		next->next = NULL;
+		dev_priv->tail = next;
+	} else {
+		/* Normally aged buffer, put it on the head + 1,
+		 * as the real head is a sentinal element
+		 */
+		next = buf_priv->my_freelist;
+		head = dev_priv->head;
+		prev = head->next;
+		head->next = next;
+		prev->prev = next;
+		next->prev = head;
+		next->next = prev;
+	}
+
+   	return 0;
+}
+
+static int mga_init_primary_bufs(drm_device_t *dev, drm_mga_init_t *init)
+{
+   	drm_mga_private_t *dev_priv = dev->dev_private;
+	drm_mga_prim_buf_t *prim_buffer;
+   	int i, temp, size_of_buf;
+   	int offset = init->reserved_map_agpstart;
+
+   	dev_priv->primary_size = ((init->primary_size + PAGE_SIZE - 1) /
+				  PAGE_SIZE) * PAGE_SIZE;
+   	size_of_buf = dev_priv->primary_size / MGA_NUM_PRIM_BUFS;
+	dev_priv->warp_ucode_size = init->warp_ucode_size;
+   	dev_priv->prim_bufs = drm_alloc(sizeof(drm_mga_prim_buf_t *) *
+					(MGA_NUM_PRIM_BUFS + 1),
+					DRM_MEM_DRIVER);
+   	if(dev_priv->prim_bufs == NULL) {
+		DRM_ERROR("Unable to allocate memory for prim_buf\n");
+		return -ENOMEM;
+	}
+   	memset(dev_priv->prim_bufs,
+	       0, sizeof(drm_mga_prim_buf_t *) * (MGA_NUM_PRIM_BUFS + 1));
+
+   	temp = init->warp_ucode_size + dev_priv->primary_size;
+	temp = ((temp + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;
+
+	dev_priv->ioremap = drm_ioremap(dev->agp->base + offset,
+					temp);
+	if(dev_priv->ioremap == NULL) {
+		DRM_ERROR("Ioremap failed\n");
+		return -ENOMEM;
+	}
+   	init_waitqueue_head(&dev_priv->wait_queue);
+
+   	for(i = 0; i < MGA_NUM_PRIM_BUFS; i++) {
+	   	prim_buffer = drm_alloc(sizeof(drm_mga_prim_buf_t),
+					DRM_MEM_DRIVER);
+	   	if(prim_buffer == NULL) return -ENOMEM;
+	   	memset(prim_buffer, 0, sizeof(drm_mga_prim_buf_t));
+	   	prim_buffer->phys_head = offset + dev->agp->base;
+	   	prim_buffer->current_dma_ptr =
+			prim_buffer->head =
+			(u32 *) (dev_priv->ioremap +
+				 offset -
+				 init->reserved_map_agpstart);
+	   	prim_buffer->num_dwords = 0;
+	   	prim_buffer->max_dwords = size_of_buf / sizeof(u32);
+	   	prim_buffer->max_dwords -= 5; /* Leave room for the softrap */
+	   	prim_buffer->sec_used = 0;
+	   	prim_buffer->idx = i;
+		prim_buffer->prim_age = i + 1;
+	   	offset = offset + size_of_buf;
+	   	dev_priv->prim_bufs[i] = prim_buffer;
+	}
+	dev_priv->current_prim_idx = 0;
+        dev_priv->next_prim =
+		dev_priv->last_prim =
+		dev_priv->current_prim =
+        	dev_priv->prim_bufs[0];
+	dev_priv->next_prim_age = 2;
+	dev_priv->last_prim_age = 1;
+   	set_bit(MGA_BUF_IN_USE, &dev_priv->current_prim->buffer_status);
+   	return 0;
+}
+
+void mga_fire_primary(drm_device_t *dev, drm_mga_prim_buf_t *prim)
+{
+       	drm_mga_private_t *dev_priv = dev->dev_private;
+      	drm_device_dma_t  *dma	    = dev->dma;
+       	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ 	int use_agp = PDEA_pagpxfer_enable;
+	unsigned long end;
+   	int i;
+   	int next_idx;
+       	PRIMLOCALS;
+
+   	dev_priv->last_prim = prim;
+
+ 	/* We never check for overflow, b/c there is always room */
+    	PRIMPTR(prim);
+   	if(num_dwords <= 0) {
+		DRM_ERROR("num_dwords == 0 when dispatched\n");
+		goto out_prim_wait;
+	}
+ 	PRIMOUTREG( MGAREG_DMAPAD, 0);
+ 	PRIMOUTREG( MGAREG_DMAPAD, 0);
+       	PRIMOUTREG( MGAREG_DMAPAD, 0);
+   	PRIMOUTREG( MGAREG_SOFTRAP, 0);
+    	PRIMFINISH(prim);
+
+	end = jiffies + (HZ*3);
+    	if(sarea_priv->dirty & MGA_DMA_FLUSH) {
+		while((MGA_READ(MGAREG_STATUS) & 0x00030001) != 0x00020000) {
+			if((signed)(end - jiffies) <= 0) {
+				DRM_ERROR("irqs: %d wanted %d\n",
+					  atomic_read(&dev->total_irq),
+					  atomic_read(&dma->total_lost));
+				DRM_ERROR("lockup (flush)\n");
+				goto out_prim_wait;
+			}
+
+			for (i = 0 ; i < 4096 ; i++) mga_delay();
+		}
+		sarea_priv->dirty &= ~(MGA_DMA_FLUSH);
+	} else {
+		while((MGA_READ(MGAREG_STATUS) & 0x00020001) != 0x00020000) {
+			if((signed)(end - jiffies) <= 0) {
+				DRM_ERROR("irqs: %d wanted %d\n",
+					  atomic_read(&dev->total_irq),
+					  atomic_read(&dma->total_lost));
+				DRM_ERROR("lockup (wait)\n");
+				goto out_prim_wait;
+			}
+
+			for (i = 0 ; i < 4096 ; i++) mga_delay();
+		}
+	}
+
+   	mga_flush_write_combine();
+    	atomic_inc(&dev_priv->pending_bufs);
+       	MGA_WRITE(MGAREG_PRIMADDRESS, phys_head | TT_GENERAL);
+ 	MGA_WRITE(MGAREG_PRIMEND, (phys_head + num_dwords * 4) | use_agp);
+   	prim->num_dwords = 0;
+	sarea_priv->last_enqueue = prim->prim_age;
+
+   	next_idx = prim->idx + 1;
+    	if(next_idx >= MGA_NUM_PRIM_BUFS)
+		next_idx = 0;
+
+    	dev_priv->next_prim = dev_priv->prim_bufs[next_idx];
+	return;
+
+ out_prim_wait:
+	prim->num_dwords = 0;
+	prim->sec_used = 0;
+	clear_bit(MGA_BUF_IN_USE, &prim->buffer_status);
+   	wake_up_interruptible(&dev_priv->wait_queue);
+	clear_bit(MGA_BUF_SWAP_PENDING, &prim->buffer_status);
+	clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status);
+}
+
+int mga_advance_primary(drm_device_t *dev)
+{
+   	DECLARE_WAITQUEUE(entry, current);
+   	drm_mga_private_t *dev_priv = dev->dev_private;
+   	drm_mga_prim_buf_t *prim_buffer;
+   	drm_device_dma_t  *dma      = dev->dma;
+   	int next_prim_idx;
+   	int ret = 0;
+
+   	/* This needs to reset the primary buffer if available,
+	 * we should collect stats on how many times it bites
+	 * it's tail */
+
+   	next_prim_idx = dev_priv->current_prim_idx + 1;
+   	if(next_prim_idx >= MGA_NUM_PRIM_BUFS)
+     		next_prim_idx = 0;
+   	prim_buffer = dev_priv->prim_bufs[next_prim_idx];
+	set_bit(MGA_IN_WAIT, &dev_priv->dispatch_status);
+
+      	/* In use is cleared in interrupt handler */
+
+   	if(test_and_set_bit(MGA_BUF_IN_USE, &prim_buffer->buffer_status)) {
+	   	add_wait_queue(&dev_priv->wait_queue, &entry);
+	   	for (;;) {
+			current->state = TASK_INTERRUPTIBLE;
+		   	mga_dma_schedule(dev, 0);
+		   	if(!test_and_set_bit(MGA_BUF_IN_USE,
+					     &prim_buffer->buffer_status))
+				break;
+		   	atomic_inc(&dev->total_sleeps);
+		   	atomic_inc(&dma->total_missed_sched);
+		   	schedule();
+		   	if (signal_pending(current)) {
+			   	ret = -ERESTARTSYS;
+			   	break;
+			}
+		}
+		current->state = TASK_RUNNING;
+	   	remove_wait_queue(&dev_priv->wait_queue, &entry);
+	   	if(ret) return ret;
+	}
+	clear_bit(MGA_IN_WAIT, &dev_priv->dispatch_status);
+
+   	/* This primary buffer is now free to use */
+   	prim_buffer->current_dma_ptr = prim_buffer->head;
+   	prim_buffer->num_dwords = 0;
+   	prim_buffer->sec_used = 0;
+	prim_buffer->prim_age = dev_priv->next_prim_age++;
+	if(prim_buffer->prim_age == 0 || prim_buffer->prim_age == 0xffffffff) {
+		mga_flush_queue(dev);
+		mga_dma_quiescent(dev);
+		mga_reset_freelist(dev);
+		prim_buffer->prim_age = (dev_priv->next_prim_age += 2);
+	}
+
+	/* Reset all buffer status stuff */
+	clear_bit(MGA_BUF_NEEDS_OVERFLOW, &prim_buffer->buffer_status);
+	clear_bit(MGA_BUF_FORCE_FIRE, &prim_buffer->buffer_status);
+	clear_bit(MGA_BUF_SWAP_PENDING, &prim_buffer->buffer_status);
+
+   	dev_priv->current_prim = prim_buffer;
+   	dev_priv->current_prim_idx = next_prim_idx;
+   	return 0;
+}
+
+/* More dynamic performance decisions */
+static inline int mga_decide_to_fire(drm_device_t *dev)
+{
+   	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+
+   	if(test_bit(MGA_BUF_FORCE_FIRE, &dev_priv->next_prim->buffer_status)) {
+	   	return 1;
+	}
+
+	if (test_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status) &&
+	    dev_priv->next_prim->num_dwords) {
+	   	return 1;
+	}
+
+	if (test_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status) &&
+	    dev_priv->next_prim->num_dwords) {
+	   	return 1;
+	}
+
+   	if(atomic_read(&dev_priv->pending_bufs) <= MGA_NUM_PRIM_BUFS - 1) {
+		if(test_bit(MGA_BUF_SWAP_PENDING,
+			    &dev_priv->next_prim->buffer_status)) {
+			return 1;
+		}
+	}
+
+   	if(atomic_read(&dev_priv->pending_bufs) <= MGA_NUM_PRIM_BUFS / 2) {
+		if(dev_priv->next_prim->sec_used >= MGA_DMA_BUF_NR / 8) {
+			return 1;
+		}
+	}
+
+   	if(atomic_read(&dev_priv->pending_bufs) >= MGA_NUM_PRIM_BUFS / 2) {
+		if(dev_priv->next_prim->sec_used >= MGA_DMA_BUF_NR / 4) {
+			return 1;
+		}
+	}
+
+   	return 0;
+}
+
+int mga_dma_schedule(drm_device_t *dev, int locked)
+{
+      	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+	int               retval    = 0;
+
+   	if (!dev_priv) return -EBUSY;
+	
+	if (test_and_set_bit(0, &dev->dma_flag)) {
+		retval = -EBUSY;
+		goto sch_out_wakeup;
+	}
+
+   	if(test_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status) ||
+	   test_bit(MGA_IN_WAIT, &dev_priv->dispatch_status) ||
+	   test_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status)) {
+		locked = 1;
+	}
+
+   	if (!locked &&
+	    !drm_lock_take(&dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT)) {
+	   	clear_bit(0, &dev->dma_flag);
+		retval = -EBUSY;
+		goto sch_out_wakeup;
+	}
+
+   	if(!test_and_set_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status)) {
+	   	/* Fire dma buffer */
+	   	if(mga_decide_to_fire(dev)) {
+			clear_bit(MGA_BUF_FORCE_FIRE,
+				  &dev_priv->next_prim->buffer_status);
+		   	if(dev_priv->current_prim == dev_priv->next_prim) {
+				/* Schedule overflow for a later time */
+				set_bit(MGA_BUF_NEEDS_OVERFLOW,
+					&dev_priv->next_prim->buffer_status);
+			}
+		   	mga_fire_primary(dev, dev_priv->next_prim);
+		} else {
+			clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status);
+		}
+	}
+
+	if (!locked) {
+		if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				  DRM_KERNEL_CONTEXT)) {
+			DRM_ERROR("\n");
+		}
+	}
+
+	clear_bit(0, &dev->dma_flag);
+
+sch_out_wakeup:
+      	if(test_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status) &&
+	   atomic_read(&dev_priv->pending_bufs) == 0) {
+		/* Everything has been processed by the hardware */
+		clear_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status);
+		wake_up_interruptible(&dev_priv->flush_queue);
+	}
+
+	if(test_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status)
+	   && dev_priv->tail->age < dev_priv->last_prim_age)
+		wake_up_interruptible(&dev_priv->buf_queue);
+
+	return retval;
+}
+
+static void mga_dma_service(int irq, void *device, struct pt_regs *regs)
+{
+    	drm_device_t	 *dev = (drm_device_t *)device;
+    	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+    	drm_mga_prim_buf_t *last_prim_buffer;
+
+    	atomic_inc(&dev->total_irq);
+	if((MGA_READ(MGAREG_STATUS) & 0x00000001) != 0x00000001) return;
+      	MGA_WRITE(MGAREG_ICLEAR, 0x00000001);
+   	last_prim_buffer = dev_priv->last_prim;
+    	last_prim_buffer->num_dwords = 0;
+    	last_prim_buffer->sec_used = 0;
+	dev_priv->sarea_priv->last_dispatch =
+		dev_priv->last_prim_age = last_prim_buffer->prim_age;
+      	clear_bit(MGA_BUF_IN_USE, &last_prim_buffer->buffer_status);
+      	clear_bit(MGA_BUF_SWAP_PENDING, &last_prim_buffer->buffer_status);
+      	clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status);
+      	atomic_dec(&dev_priv->pending_bufs);
+   	queue_task(&dev->tq, &tq_immediate);
+   	mark_bh(IMMEDIATE_BH);
+   	wake_up_interruptible(&dev_priv->wait_queue);
+}
+
+static void mga_dma_task_queue(void *device)
+{
+	mga_dma_schedule((drm_device_t *)device, 0);
+}
+
+int mga_dma_cleanup(drm_device_t *dev)
+{
+	if(dev->dev_private) {
+		drm_mga_private_t *dev_priv =
+			(drm_mga_private_t *) dev->dev_private;
+
+		if (dev->irq) mga_flush_queue(dev);
+		mga_dma_quiescent(dev);
+
+		if(dev_priv->ioremap) {
+			int temp = (dev_priv->warp_ucode_size +
+				    dev_priv->primary_size +
+				    PAGE_SIZE - 1) / PAGE_SIZE * PAGE_SIZE;
+
+			drm_ioremapfree((void *) dev_priv->ioremap, temp);
+		}
+	   	if(dev_priv->status_page != NULL) {
+		   	iounmap(dev_priv->status_page);
+		}
+	   	if(dev_priv->real_status_page != 0UL) {
+		   	mga_free_page(dev, dev_priv->real_status_page);
+		}
+	   	if(dev_priv->prim_bufs != NULL) {
+		   	int i;
+		   	for(i = 0; i < MGA_NUM_PRIM_BUFS; i++) {
+			   	if(dev_priv->prim_bufs[i] != NULL) {
+			     		drm_free(dev_priv->prim_bufs[i],
+						 sizeof(drm_mga_prim_buf_t),
+						 DRM_MEM_DRIVER);
+				}
+			}
+		   	drm_free(dev_priv->prim_bufs, sizeof(void *) *
+				 (MGA_NUM_PRIM_BUFS + 1),
+				 DRM_MEM_DRIVER);
+		}
+		if(dev_priv->head != NULL) {
+		   	mga_freelist_cleanup(dev);
+		}
+
+
+		drm_free(dev->dev_private, sizeof(drm_mga_private_t),
+			 DRM_MEM_DRIVER);
+		dev->dev_private = NULL;
+	}
+
+	return 0;
+}
+
+static int mga_dma_initialize(drm_device_t *dev, drm_mga_init_t *init) {
+	drm_mga_private_t *dev_priv;
+	drm_map_t *sarea_map = NULL;
+
+	dev_priv = drm_alloc(sizeof(drm_mga_private_t), DRM_MEM_DRIVER);
+	if(dev_priv == NULL) return -ENOMEM;
+	dev->dev_private = (void *) dev_priv;
+
+	memset(dev_priv, 0, sizeof(drm_mga_private_t));
+
+	if((init->reserved_map_idx >= dev->map_count) ||
+	   (init->buffer_map_idx >= dev->map_count)) {
+		mga_dma_cleanup(dev);
+		return -EINVAL;
+	}
+
+	dev_priv->reserved_map_idx = init->reserved_map_idx;
+	dev_priv->buffer_map_idx = init->buffer_map_idx;
+	sarea_map = dev->maplist[0];
+	dev_priv->sarea_priv = (drm_mga_sarea_t *)
+		((u8 *)sarea_map->handle +
+		 init->sarea_priv_offset);
+
+	/* Scale primary size to the next page */
+	dev_priv->chipset = init->chipset;
+	dev_priv->frontOffset = init->frontOffset;
+	dev_priv->backOffset = init->backOffset;
+	dev_priv->depthOffset = init->depthOffset;
+	dev_priv->textureOffset = init->textureOffset;
+	dev_priv->textureSize = init->textureSize;
+	dev_priv->cpp = init->cpp;
+	dev_priv->sgram = init->sgram;
+	dev_priv->stride = init->stride;
+
+	dev_priv->mAccess = init->mAccess;
+   	init_waitqueue_head(&dev_priv->flush_queue);
+	init_waitqueue_head(&dev_priv->buf_queue);
+	dev_priv->WarpPipe = 0xff000000;
+	dev_priv->vertexsize = 0;
+
+   	DRM_DEBUG("chipset=%d ucode_size=%d backOffset=%x depthOffset=%x\n",
+		  dev_priv->chipset, dev_priv->warp_ucode_size,
+		  dev_priv->backOffset, dev_priv->depthOffset);
+   	DRM_DEBUG("cpp: %d sgram: %d stride: %d maccess: %x\n",
+		  dev_priv->cpp, dev_priv->sgram, dev_priv->stride,
+		  dev_priv->mAccess);
+
+	memcpy(&dev_priv->WarpIndex, &init->WarpIndex,
+	       sizeof(drm_mga_warp_index_t) * MGA_MAX_WARP_PIPES);
+
+   	if(mga_init_primary_bufs(dev, init) != 0) {
+		DRM_ERROR("Can not initialize primary buffers\n");
+		mga_dma_cleanup(dev);
+		return -ENOMEM;
+	}
+   	dev_priv->real_status_page = mga_alloc_page(dev);
+      	if(dev_priv->real_status_page == 0UL) {
+		mga_dma_cleanup(dev);
+		DRM_ERROR("Can not allocate status page\n");
+		return -ENOMEM;
+	}
+
+   	dev_priv->status_page =
+		ioremap_nocache(virt_to_bus((void *)dev_priv->real_status_page),
+				PAGE_SIZE);
+
+   	if(dev_priv->status_page == NULL) {
+		mga_dma_cleanup(dev);
+		DRM_ERROR("Can not remap status page\n");
+		return -ENOMEM;
+	}
+
+   	/* Write status page when secend or softrap occurs */
+   	MGA_WRITE(MGAREG_PRIMPTR,
+		  virt_to_bus((void *)dev_priv->real_status_page) | 0x00000003);
+
+
+	/* Private is now filled in, initialize the hardware */
+	{
+		PRIMLOCALS;
+		PRIMGETPTR( dev_priv );
+
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+		PRIMOUTREG(MGAREG_DWGSYNC, 0x0100);
+		PRIMOUTREG(MGAREG_SOFTRAP, 0);
+		/* Poll for the first buffer to insure that
+		 * the status register will be correct
+		 */
+
+		mga_flush_write_combine();
+	   	MGA_WRITE(MGAREG_PRIMADDRESS, phys_head | TT_GENERAL);
+
+		MGA_WRITE(MGAREG_PRIMEND, ((phys_head + num_dwords * 4) |
+					   PDEA_pagpxfer_enable));
+
+	   	while(MGA_READ(MGAREG_DWGSYNC) != 0x0100) ;
+	}
+
+	if(mga_freelist_init(dev) != 0) {
+	   	DRM_ERROR("Could not initialize freelist\n");
+	   	mga_dma_cleanup(dev);
+	   	return -ENOMEM;
+	}
+	return 0;
+}
+
+int mga_dma_init(struct inode *inode, struct file *filp,
+		 unsigned int cmd, unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_mga_init_t init;
+
+	if (copy_from_user(&init, (drm_mga_init_t *)arg, sizeof(init)))
+		return -EFAULT;
+
+	switch(init.func) {
+	case MGA_INIT_DMA:
+		return mga_dma_initialize(dev, &init);
+	case MGA_CLEANUP_DMA:
+		return mga_dma_cleanup(dev);
+	}
+
+	return -EINVAL;
+}
+
+int mga_irq_install(drm_device_t *dev, int irq)
+{
+	int retcode;
+
+	if (!irq)     return -EINVAL;
+
+	down(&dev->struct_sem);
+	if (dev->irq) {
+		up(&dev->struct_sem);
+		return -EBUSY;
+	}
+	dev->irq = irq;
+	up(&dev->struct_sem);
+
+	DRM_DEBUG("install irq handler %d\n", irq);
+
+	dev->context_flag     = 0;
+	dev->interrupt_flag   = 0;
+	dev->dma_flag	      = 0;
+	dev->dma->next_buffer = NULL;
+	dev->dma->next_queue  = NULL;
+	dev->dma->this_buffer = NULL;
+	INIT_LIST_HEAD(&dev->tq.list);
+	dev->tq.sync	      = 0;
+	dev->tq.routine	      = mga_dma_task_queue;
+	dev->tq.data	      = dev;
+
+				/* Before installing handler */
+	MGA_WRITE(MGAREG_IEN, 0);
+   				/* Install handler */
+	if ((retcode = request_irq(dev->irq,
+				   mga_dma_service,
+				   SA_SHIRQ,
+				   dev->devname,
+				   dev))) {
+		down(&dev->struct_sem);
+		dev->irq = 0;
+		up(&dev->struct_sem);
+		return retcode;
+	}
+				/* After installing handler */
+   	MGA_WRITE(MGAREG_ICLEAR, 0x00000001);
+	MGA_WRITE(MGAREG_IEN, 0x00000001);
+	return 0;
+}
+
+int mga_irq_uninstall(drm_device_t *dev)
+{
+	int irq;
+
+	down(&dev->struct_sem);
+	irq	 = dev->irq;
+	dev->irq = 0;
+	up(&dev->struct_sem);
+
+	if (!irq) return -EINVAL;
+   	DRM_DEBUG("remove irq handler %d\n", irq);
+      	MGA_WRITE(MGAREG_ICLEAR, 0x00000001);
+	MGA_WRITE(MGAREG_IEN, 0);
+	free_irq(irq, dev);
+	return 0;
+}
+
+int mga_control(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_control_t	ctl;
+
+	if (copy_from_user(&ctl, (drm_control_t *)arg, sizeof(ctl)))
+		return -EFAULT;
+
+	switch (ctl.func) {
+	case DRM_INST_HANDLER:
+		return mga_irq_install(dev, ctl.irq);
+	case DRM_UNINST_HANDLER:
+		return mga_irq_uninstall(dev);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int mga_flush_queue(drm_device_t *dev)
+{
+   	DECLARE_WAITQUEUE(entry, current);
+  	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+   	int ret = 0;
+
+   	if(!dev_priv) return 0;
+
+   	if(dev_priv->next_prim->num_dwords != 0) {
+   		add_wait_queue(&dev_priv->flush_queue, &entry);
+		if (test_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status)) 
+			DRM_ERROR("Incorrect mga_flush_queue logic\n");
+		set_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status);
+		mga_dma_schedule(dev, 0);
+   		for (;;) {
+			current->state = TASK_INTERRUPTIBLE;
+	   		if (!test_bit(MGA_IN_FLUSH,
+				      &dev_priv->dispatch_status))
+				break;
+		   	atomic_inc(&dev->total_sleeps);
+	      		schedule();
+	      		if (signal_pending(current)) {
+		   		ret = -EINTR; /* Can't restart */
+				clear_bit(MGA_IN_FLUSH,
+					  &dev_priv->dispatch_status);
+		   		break;
+			}
+		}
+		current->state = TASK_RUNNING;
+   		remove_wait_queue(&dev_priv->flush_queue, &entry);
+	}
+   	return ret;
+}
+
+/* Must be called with the lock held */
+void mga_reclaim_buffers(drm_device_t *dev, pid_t pid)
+{
+	drm_device_dma_t *dma = dev->dma;
+	int		 i;
+
+	if (!dma) return;
+      	if(dev->dev_private == NULL) return;
+	if(dma->buflist == NULL) return;
+
+	DRM_DEBUG("buf_count=%d\n", dma->buf_count);
+	
+        mga_flush_queue(dev);
+
+	for (i = 0; i < dma->buf_count; i++) {
+	   	drm_buf_t *buf = dma->buflist[ i ];
+	   	drm_mga_buf_priv_t *buf_priv = buf->dev_private;
+
+		/* Only buffers that need to get reclaimed ever
+		 * get set to free
+		 */
+		if (buf->pid == pid  && buf_priv) {
+			if(buf_priv->my_freelist->age == MGA_BUF_USED)
+		     		buf_priv->my_freelist->age = MGA_BUF_FREE;
+		}
+	}
+}
+
+int mga_lock(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	DECLARE_WAITQUEUE(entry, current);
+	int		  ret	= 0;
+	drm_lock_t	  lock;
+
+	if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)))
+		return -EFAULT;
+
+	if (lock.context == DRM_KERNEL_CONTEXT) {
+		DRM_ERROR("Process %d using kernel context %d\n",
+			  current->pid, lock.context);
+		return -EINVAL;
+	}
+
+	if (lock.context < 0) return -EINVAL;
+
+	/* Only one queue:
+	 */
+
+	if (!ret) {
+		add_wait_queue(&dev->lock.lock_queue, &entry);
+		for (;;) {
+			current->state = TASK_INTERRUPTIBLE;
+			if (!dev->lock.hw_lock) {
+				/* Device has been unregistered */
+				ret = -EINTR;
+				break;
+			}
+			if (drm_lock_take(&dev->lock.hw_lock->lock,
+					  lock.context)) {
+				dev->lock.pid	    = current->pid;
+				dev->lock.lock_time = jiffies;
+				atomic_inc(&dev->total_locks);
+				break;	/* Got lock */
+			}
+
+				/* Contention */
+			atomic_inc(&dev->total_sleeps);
+			schedule();
+			if (signal_pending(current)) {
+				ret = -ERESTARTSYS;
+				break;
+			}
+		}
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&dev->lock.lock_queue, &entry);
+	}
+
+	if (!ret) {
+		sigemptyset(&dev->sigmask);
+		sigaddset(&dev->sigmask, SIGSTOP);
+		sigaddset(&dev->sigmask, SIGTSTP);
+		sigaddset(&dev->sigmask, SIGTTIN);
+		sigaddset(&dev->sigmask, SIGTTOU);
+		dev->sigdata.context = lock.context;
+		dev->sigdata.lock    = dev->lock.hw_lock;
+		block_all_signals(drm_notifier, &dev->sigdata, &dev->sigmask);
+
+		if (lock.flags & _DRM_LOCK_QUIESCENT) {
+		   DRM_DEBUG("_DRM_LOCK_QUIESCENT\n");
+		   mga_flush_queue(dev);
+		   mga_dma_quiescent(dev);
+		}
+	}
+
+	if (ret) DRM_DEBUG("%d %s\n", lock.context,
+			   ret ? "interrupted" : "has lock");
+	return ret;
+}
+
+int mga_flush_ioctl(struct inode *inode, struct file *filp,
+		    unsigned int cmd, unsigned long arg)
+{
+       	drm_file_t	  *priv	  = filp->private_data;
+    	drm_device_t	  *dev	  = priv->dev;
+	drm_lock_t	  lock;
+      	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+
+	if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)))
+		return -EFAULT;
+
+	if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("lock not held\n");
+		return -EINVAL;
+	}
+
+   	if(lock.flags & _DRM_LOCK_FLUSH || lock.flags & _DRM_LOCK_FLUSH_ALL) {
+		drm_mga_prim_buf_t *temp_buf;
+
+		temp_buf = dev_priv->current_prim;
+
+		if(temp_buf && temp_buf->num_dwords) {
+			set_bit(MGA_BUF_FORCE_FIRE, &temp_buf->buffer_status);
+			mga_advance_primary(dev);
+ 		}
+		mga_dma_schedule(dev, 1);
+	}
+   	if(lock.flags & _DRM_LOCK_QUIESCENT) {
+		mga_flush_queue(dev);
+		mga_dma_quiescent(dev);
+	}
+
+    	return 0;
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/mga_drm.h linux/drivers/char/drm-4.0/mga_drm.h
--- linux-2.4.18-pre2/drivers/char/drm-4.0/mga_drm.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/mga_drm.h	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,274 @@
+/* mga_drm.h -- Public header for the Matrox g200/g400 driver -*- linux-c -*-
+ * Created: Tue Jan 25 01:50:01 1999 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Jeff Hartmann <jhartmann@valinux.com>
+ *          Keith Whitwell <keithw@valinux.com>
+ *
+ */
+
+#ifndef _MGA_DRM_H_
+#define _MGA_DRM_H_
+
+/* WARNING: If you change any of these defines, make sure to change the
+ * defines in the Xserver file (xf86drmMga.h)
+ */
+#ifndef _MGA_DEFINES_
+#define _MGA_DEFINES_
+
+#define MGA_F  0x1		/* fog */
+#define MGA_A  0x2		/* alpha */
+#define MGA_S  0x4		/* specular */
+#define MGA_T2 0x8		/* multitexture */
+
+#define MGA_WARP_TGZ            0
+#define MGA_WARP_TGZF           (MGA_F)
+#define MGA_WARP_TGZA           (MGA_A)
+#define MGA_WARP_TGZAF          (MGA_F|MGA_A)
+#define MGA_WARP_TGZS           (MGA_S)
+#define MGA_WARP_TGZSF          (MGA_S|MGA_F)
+#define MGA_WARP_TGZSA          (MGA_S|MGA_A)
+#define MGA_WARP_TGZSAF         (MGA_S|MGA_F|MGA_A)
+#define MGA_WARP_T2GZ           (MGA_T2)
+#define MGA_WARP_T2GZF          (MGA_T2|MGA_F)
+#define MGA_WARP_T2GZA          (MGA_T2|MGA_A)
+#define MGA_WARP_T2GZAF         (MGA_T2|MGA_A|MGA_F)
+#define MGA_WARP_T2GZS          (MGA_T2|MGA_S)
+#define MGA_WARP_T2GZSF         (MGA_T2|MGA_S|MGA_F)
+#define MGA_WARP_T2GZSA         (MGA_T2|MGA_S|MGA_A)
+#define MGA_WARP_T2GZSAF        (MGA_T2|MGA_S|MGA_F|MGA_A)
+
+#define MGA_MAX_G400_PIPES 16
+#define MGA_MAX_G200_PIPES  8	/* no multitex */
+#define MGA_MAX_WARP_PIPES MGA_MAX_G400_PIPES
+
+#define MGA_CARD_TYPE_G200 1
+#define MGA_CARD_TYPE_G400 2
+
+#define MGA_FRONT   0x1
+#define MGA_BACK    0x2
+#define MGA_DEPTH   0x4
+
+/* 3d state excluding texture units:
+ */
+#define MGA_CTXREG_DSTORG     0	/* validated */
+#define MGA_CTXREG_MACCESS    1	
+#define MGA_CTXREG_PLNWT      2 	
+#define MGA_CTXREG_DWGCTL     3	
+#define MGA_CTXREG_ALPHACTRL  4
+#define MGA_CTXREG_FOGCOLOR   5
+#define MGA_CTXREG_WFLAG      6
+#define MGA_CTXREG_TDUAL0     7
+#define MGA_CTXREG_TDUAL1     8
+#define MGA_CTXREG_FCOL       9
+#define MGA_CTXREG_STENCIL    10
+#define MGA_CTXREG_STENCILCTL 11
+#define MGA_CTX_SETUP_SIZE    12
+
+/* 2d state
+ */
+#define MGA_2DREG_PITCH 	0
+#define MGA_2D_SETUP_SIZE 	1
+
+/* Each texture unit has a state:
+ */
+#define MGA_TEXREG_CTL        0
+#define MGA_TEXREG_CTL2       1
+#define MGA_TEXREG_FILTER     2
+#define MGA_TEXREG_BORDERCOL  3
+#define MGA_TEXREG_ORG        4 /* validated */
+#define MGA_TEXREG_ORG1       5
+#define MGA_TEXREG_ORG2       6
+#define MGA_TEXREG_ORG3       7
+#define MGA_TEXREG_ORG4       8
+#define MGA_TEXREG_WIDTH      9
+#define MGA_TEXREG_HEIGHT     10
+#define MGA_TEX_SETUP_SIZE    11
+
+/* What needs to be changed for the current vertex dma buffer?
+ */
+#define MGA_UPLOAD_CTX        0x1
+#define MGA_UPLOAD_TEX0       0x2
+#define MGA_UPLOAD_TEX1       0x4
+#define MGA_UPLOAD_PIPE       0x8
+#define MGA_UPLOAD_TEX0IMAGE  0x10 /* handled client-side */
+#define MGA_UPLOAD_TEX1IMAGE  0x20 /* handled client-side */
+#define MGA_UPLOAD_2D 	      0x40
+#define MGA_WAIT_AGE          0x80 /* handled client-side */
+#define MGA_UPLOAD_CLIPRECTS  0x100 /* handled client-side */
+#define MGA_DMA_FLUSH	      0x200 /* set when someone gets the lock
+                                       quiescent */
+
+/* 32 buffers of 64k each, total 2 meg.
+ */
+#define MGA_DMA_BUF_ORDER     16
+#define MGA_DMA_BUF_SZ        (1<<MGA_DMA_BUF_ORDER)
+#define MGA_DMA_BUF_NR        31
+
+/* Keep these small for testing.
+ */
+#define MGA_NR_SAREA_CLIPRECTS 8
+
+/* 2 heaps (1 for card, 1 for agp), each divided into upto 128
+ * regions, subject to a minimum region size of (1<<16) == 64k. 
+ *
+ * Clients may subdivide regions internally, but when sharing between
+ * clients, the region size is the minimum granularity. 
+ */
+
+#define MGA_CARD_HEAP 0
+#define MGA_AGP_HEAP  1
+#define MGA_NR_TEX_HEAPS 2
+#define MGA_NR_TEX_REGIONS 16
+#define MGA_LOG_MIN_TEX_REGION_SIZE 16
+#endif
+
+typedef struct _drm_mga_warp_index {
+   	int installed;
+   	unsigned long phys_addr;
+   	int size;
+} drm_mga_warp_index_t;
+
+typedef struct drm_mga_init {
+   	enum { 
+	   	MGA_INIT_DMA = 0x01,
+	       	MGA_CLEANUP_DMA = 0x02
+	} func;
+   	int reserved_map_agpstart;
+   	int reserved_map_idx;
+   	int buffer_map_idx;
+   	int sarea_priv_offset;
+   	int primary_size;
+   	int warp_ucode_size;
+   	unsigned int frontOffset;
+   	unsigned int backOffset;
+   	unsigned int depthOffset;
+   	unsigned int textureOffset;
+   	unsigned int textureSize;
+        unsigned int agpTextureOffset;
+        unsigned int agpTextureSize;
+   	unsigned int cpp;
+   	unsigned int stride;
+   	int sgram;
+	int chipset;
+   	drm_mga_warp_index_t WarpIndex[MGA_MAX_WARP_PIPES];
+	unsigned int mAccess;
+} drm_mga_init_t;
+
+/* Warning: if you change the sarea structure, you must change the Xserver
+ * structures as well */
+
+typedef struct _drm_mga_tex_region {
+	unsigned char next, prev;	
+	unsigned char in_use;	
+	unsigned int age;			
+} drm_mga_tex_region_t;
+
+typedef struct _drm_mga_sarea {
+	/* The channel for communication of state information to the kernel
+	 * on firing a vertex dma buffer.
+	 */
+   	unsigned int ContextState[MGA_CTX_SETUP_SIZE];
+   	unsigned int ServerState[MGA_2D_SETUP_SIZE];
+   	unsigned int TexState[2][MGA_TEX_SETUP_SIZE];
+   	unsigned int WarpPipe;
+   	unsigned int dirty;
+
+   	unsigned int nbox;
+   	drm_clip_rect_t boxes[MGA_NR_SAREA_CLIPRECTS];
+
+
+	/* Information about the most recently used 3d drawable.  The
+	 * client fills in the req_* fields, the server fills in the 
+	 * exported_ fields and puts the cliprects into boxes, above.
+	 *
+	 * The client clears the exported_drawable field before
+	 * clobbering the boxes data.
+	 */
+        unsigned int req_drawable;	 /* the X drawable id */
+	unsigned int req_draw_buffer;	 /* MGA_FRONT or MGA_BACK */
+
+        unsigned int exported_drawable;	 
+	unsigned int exported_index; 
+        unsigned int exported_stamp;	
+        unsigned int exported_buffers;	 
+        unsigned int exported_nfront;
+        unsigned int exported_nback;
+	int exported_back_x, exported_front_x, exported_w;	
+	int exported_back_y, exported_front_y, exported_h;
+   	drm_clip_rect_t exported_boxes[MGA_NR_SAREA_CLIPRECTS];
+   
+	/* Counters for aging textures and for client-side throttling.
+	 */
+        unsigned int last_enqueue;	/* last time a buffer was enqueued */
+	unsigned int last_dispatch;	/* age of the most recently dispatched buffer */
+	unsigned int last_quiescent;     /*  */
+
+
+	/* LRU lists for texture memory in agp space and on the card
+	 */
+	drm_mga_tex_region_t texList[MGA_NR_TEX_HEAPS][MGA_NR_TEX_REGIONS+1];
+	unsigned int texAge[MGA_NR_TEX_HEAPS];
+	
+	/* Mechanism to validate card state.
+	 */
+   	int ctxOwner;
+   	int vertexsize;
+} drm_mga_sarea_t;	
+
+/* Device specific ioctls:
+ */
+typedef struct _drm_mga_clear {
+	unsigned int clear_color;
+	unsigned int clear_depth;
+	unsigned int flags;
+	unsigned int clear_depth_mask;
+	unsigned int clear_color_mask;
+} drm_mga_clear_t;
+
+typedef struct _drm_mga_swap {
+   	int dummy;
+} drm_mga_swap_t;
+
+typedef struct _drm_mga_iload {
+	int idx;
+	int length;
+	unsigned int destOrg;
+} drm_mga_iload_t;
+
+typedef struct _drm_mga_vertex {
+   	int idx;		/* buffer to queue */
+	int used;		/* bytes in use */
+	int discard;		/* client finished with buffer?  */
+} drm_mga_vertex_t;
+
+typedef struct _drm_mga_indices {
+   	int idx;		/* buffer to queue */
+	unsigned int start;		
+	unsigned int end;		
+	int discard;		/* client finished with buffer?  */
+} drm_mga_indices_t;
+
+#endif
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/mga_drv.c linux/drivers/char/drm-4.0/mga_drv.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/mga_drv.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/mga_drv.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,666 @@
+/* mga_drv.c -- Matrox g200/g400 driver -*- linux-c -*-
+ * Created: Mon Dec 13 01:56:22 1999 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
+ *	    Jeff Hartmann <jhartmann@valinux.com>
+ *
+ *
+ */
+
+#include <linux/config.h>
+#include "drmP.h"
+#include "mga_drv.h"
+
+#define MGA_NAME	 "mga"
+#define MGA_DESC	 "Matrox G200/G400"
+#define MGA_DATE	 "20000928"
+#define MGA_MAJOR	 2
+#define MGA_MINOR	 0
+#define MGA_PATCHLEVEL	 1
+
+static drm_device_t	      mga_device;
+drm_ctx_t		      mga_res_ctx;
+
+static struct file_operations mga_fops = {
+#if LINUX_VERSION_CODE >= 0x020400
+				/* This started being used during 2.4.0-test */
+	owner:   THIS_MODULE,
+#endif
+	open:	 mga_open,
+	flush:	 drm_flush,
+	release: mga_release,
+	ioctl:	 mga_ioctl,
+	mmap:	 drm_mmap,
+	read:	 drm_read,
+	fasync:	 drm_fasync,
+   	poll:	 drm_poll,
+};
+
+static struct miscdevice      mga_misc = {
+	minor: MISC_DYNAMIC_MINOR,
+	name:  MGA_NAME,
+	fops:  &mga_fops,
+};
+
+static drm_ioctl_desc_t	      mga_ioctls[] = {
+	[DRM_IOCTL_NR(DRM_IOCTL_VERSION)]     = { mga_version,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)]  = { drm_getunique,  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)]   = { drm_getmagic,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)]   = { drm_irq_busid,  0, 1 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)]  = { drm_setunique,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_BLOCK)]	      = { drm_block,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)]     = { drm_unblock,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_CONTROL)]     = { mga_control,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)]  = { drm_authmagic,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)]     = { drm_addmap,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)]    = { mga_addbufs,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)]   = { mga_markbufs,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)]   = { mga_infobufs,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MAP_BUFS)]    = { mga_mapbufs,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)]   = { mga_freebufs,	  1, 0 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)]     = { mga_addctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)]      = { mga_rmctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)]     = { mga_modctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)]     = { mga_getctx,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)]  = { mga_switchctx,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)]     = { mga_newctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)]     = { mga_resctx,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)]    = { drm_adddraw,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)]     = { drm_rmdraw,	  1, 1 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_DMA)]	      = { mga_dma,	  1, 0 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_LOCK)]	      = { mga_lock,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)]      = { mga_unlock,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FINISH)]      = { drm_finish,	  1, 0 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)] = { drm_agp_acquire, 1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)] = { drm_agp_release, 1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)]  = { drm_agp_enable,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)]    = { drm_agp_info,    1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)]   = { drm_agp_alloc,   1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)]    = { drm_agp_free,    1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)]    = { drm_agp_bind,    1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)]  = { drm_agp_unbind,  1, 1 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_MGA_INIT)]    = { mga_dma_init,    1, 1 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_MGA_SWAP)]    = { mga_swap_bufs,   1, 0 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_MGA_CLEAR)]   = { mga_clear_bufs,  1, 0 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_MGA_ILOAD)]   = { mga_iload,       1, 0 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_MGA_VERTEX)]  = { mga_vertex,      1, 0 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_MGA_FLUSH)]   = { mga_flush_ioctl, 1, 0 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_MGA_INDICES)] = { mga_indices,     1, 0 },
+};
+
+#define MGA_IOCTL_COUNT DRM_ARRAY_SIZE(mga_ioctls)
+
+#ifdef MODULE
+static char		      *mga = NULL;
+#endif
+
+MODULE_AUTHOR("VA Linux Systems, Inc.");
+MODULE_DESCRIPTION("Matrox G200/G400");
+MODULE_LICENSE("GPL and additional rights");
+MODULE_PARM(mga, "s");
+
+#ifndef MODULE
+/* mga_options is called by the kernel to parse command-line options passed
+ * via the boot-loader (e.g., LILO).  It calls the insmod option routine,
+ * drm_parse_drm.
+ */
+
+static int __init mga_options(char *str)
+{
+	drm_parse_options(str);
+	return 1;
+}
+
+__setup("mga=", mga_options);
+#endif
+
+static int mga_setup(drm_device_t *dev)
+{
+	int i;
+
+	atomic_set(&dev->ioctl_count, 0);
+	atomic_set(&dev->vma_count, 0);
+	dev->buf_use	  = 0;
+	atomic_set(&dev->buf_alloc, 0);
+
+	drm_dma_setup(dev);
+
+	atomic_set(&dev->total_open, 0);
+	atomic_set(&dev->total_close, 0);
+	atomic_set(&dev->total_ioctl, 0);
+	atomic_set(&dev->total_irq, 0);
+	atomic_set(&dev->total_ctx, 0);
+	atomic_set(&dev->total_locks, 0);
+	atomic_set(&dev->total_unlocks, 0);
+	atomic_set(&dev->total_contends, 0);
+	atomic_set(&dev->total_sleeps, 0);
+
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		dev->magiclist[i].head = NULL;
+		dev->magiclist[i].tail = NULL;
+	}
+	dev->maplist	    = NULL;
+	dev->map_count	    = 0;
+	dev->vmalist	    = NULL;
+	dev->lock.hw_lock   = NULL;
+	init_waitqueue_head(&dev->lock.lock_queue);
+	dev->queue_count    = 0;
+	dev->queue_reserved = 0;
+	dev->queue_slots    = 0;
+	dev->queuelist	    = NULL;
+	dev->irq	    = 0;
+	dev->context_flag   = 0;
+	dev->interrupt_flag = 0;
+	dev->dma_flag	    = 0;
+	dev->last_context   = 0;
+	dev->last_switch    = 0;
+	dev->last_checked   = 0;
+	init_timer(&dev->timer);
+	init_waitqueue_head(&dev->context_wait);
+
+	dev->ctx_start	    = 0;
+	dev->lck_start	    = 0;
+
+	dev->buf_rp	  = dev->buf;
+	dev->buf_wp	  = dev->buf;
+	dev->buf_end	  = dev->buf + DRM_BSZ;
+	dev->buf_async	  = NULL;
+	init_waitqueue_head(&dev->buf_readers);
+	init_waitqueue_head(&dev->buf_writers);
+
+	DRM_DEBUG("\n");
+
+	/* The kernel's context could be created here, but is now created
+	   in drm_dma_enqueue.	This is more resource-efficient for
+	   hardware that does not do DMA, but may mean that
+	   drm_select_queue fails between the time the interrupt is
+	   initialized and the time the queues are initialized. */
+
+	return 0;
+}
+
+
+static int mga_takedown(drm_device_t *dev)
+{
+	int		  i;
+	drm_magic_entry_t *pt, *next;
+	drm_map_t	  *map;
+	drm_vma_entry_t	  *vma, *vma_next;
+
+	DRM_DEBUG("\n");
+
+	if (dev->dev_private) mga_dma_cleanup(dev);
+	if (dev->irq) mga_irq_uninstall(dev);
+
+	down(&dev->struct_sem);
+	del_timer(&dev->timer);
+
+	if (dev->devname) {
+		drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER);
+		dev->devname = NULL;
+	}
+
+	if (dev->unique) {
+		drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER);
+		dev->unique = NULL;
+		dev->unique_len = 0;
+	}
+				/* Clear pid list */
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		for (pt = dev->magiclist[i].head; pt; pt = next) {
+			next = pt->next;
+			drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
+		}
+		dev->magiclist[i].head = dev->magiclist[i].tail = NULL;
+	}
+   				/* Clear AGP information */
+	if (dev->agp) {
+		drm_agp_mem_t *entry;
+		drm_agp_mem_t *nexte;
+
+				/* Remove AGP resources, but leave dev->agp
+                                   intact until cleanup is called. */
+		for (entry = dev->agp->memory; entry; entry = nexte) {
+			nexte = entry->next;
+			if (entry->bound) drm_unbind_agp(entry->memory);
+			drm_free_agp(entry->memory, entry->pages);
+			drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS);
+		}
+		dev->agp->memory = NULL;
+
+		if (dev->agp->acquired)	_drm_agp_release();
+
+		dev->agp->acquired = 0;
+		dev->agp->enabled  = 0;
+	}
+				/* Clear vma list (only built for debugging) */
+	if (dev->vmalist) {
+		for (vma = dev->vmalist; vma; vma = vma_next) {
+			vma_next = vma->next;
+			drm_free(vma, sizeof(*vma), DRM_MEM_VMAS);
+		}
+		dev->vmalist = NULL;
+	}
+
+				/* Clear map area and mtrr information */
+	if (dev->maplist) {
+		for (i = 0; i < dev->map_count; i++) {
+			map = dev->maplist[i];
+			switch (map->type) {
+			case _DRM_REGISTERS:
+			case _DRM_FRAME_BUFFER:
+#ifdef CONFIG_MTRR
+				if (map->mtrr >= 0) {
+					int retcode;
+					retcode = mtrr_del(map->mtrr,
+							   map->offset,
+							   map->size);
+					DRM_DEBUG("mtrr_del = %d\n", retcode);
+				}
+#endif
+				drm_ioremapfree(map->handle, map->size);
+				break;
+			case _DRM_SHM:
+				drm_free_pages((unsigned long)map->handle,
+					       drm_order(map->size)
+					       - PAGE_SHIFT,
+					       DRM_MEM_SAREA);
+				break;
+			case _DRM_AGP:
+				break;
+			}
+			drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+		}
+		drm_free(dev->maplist,
+			 dev->map_count * sizeof(*dev->maplist),
+			 DRM_MEM_MAPS);
+		dev->maplist   = NULL;
+		dev->map_count = 0;
+	}
+
+	if (dev->queuelist) {
+		for (i = 0; i < dev->queue_count; i++) {
+			drm_waitlist_destroy(&dev->queuelist[i]->waitlist);
+			if (dev->queuelist[i]) {
+				drm_free(dev->queuelist[i],
+					 sizeof(*dev->queuelist[0]),
+					 DRM_MEM_QUEUES);
+				dev->queuelist[i] = NULL;
+			}
+		}
+		drm_free(dev->queuelist,
+			 dev->queue_slots * sizeof(*dev->queuelist),
+			 DRM_MEM_QUEUES);
+		dev->queuelist	 = NULL;
+	}
+
+	drm_dma_takedown(dev);
+
+	dev->queue_count     = 0;
+	if (dev->lock.hw_lock) {
+		dev->lock.hw_lock    = NULL; /* SHM removed */
+		dev->lock.pid	     = 0;
+		wake_up_interruptible(&dev->lock.lock_queue);
+	}
+	up(&dev->struct_sem);
+
+	return 0;
+}
+
+/* mga_init is called via init_module at module load time, or via
+ * linux/init/main.c (this is not currently supported). */
+
+static int __init mga_init(void)
+{
+	int		      retcode;
+	drm_device_t	      *dev = &mga_device;
+
+	DRM_DEBUG("\n");
+
+	memset((void *)dev, 0, sizeof(*dev));
+	dev->count_lock	  = SPIN_LOCK_UNLOCKED;
+	sema_init(&dev->struct_sem, 1);
+
+#ifdef MODULE
+	drm_parse_options(mga);
+#endif
+	if ((retcode = misc_register(&mga_misc))) {
+		DRM_ERROR("Cannot register \"%s\"\n", MGA_NAME);
+		return retcode;
+	}
+	dev->device = MKDEV(MISC_MAJOR, mga_misc.minor);
+	dev->name   = MGA_NAME;
+
+	drm_mem_init();
+	drm_proc_init(dev);
+	dev->agp    = drm_agp_init();
+      	if(dev->agp == NULL) {
+	   	DRM_INFO("The mga drm module requires the agpgart module"
+		         " to function correctly\nPlease load the agpgart"
+		         " module before you load the mga module\n");
+	   	drm_proc_cleanup();
+	   	misc_deregister(&mga_misc);
+	   	mga_takedown(dev);
+	   	return -ENOMEM;
+	}
+#ifdef CONFIG_MTRR
+   	dev->agp->agp_mtrr = mtrr_add(dev->agp->agp_info.aper_base,
+				      dev->agp->agp_info.aper_size * 1024 * 1024,
+				      MTRR_TYPE_WRCOMB,
+				      1);
+#endif
+	if((retcode = drm_ctxbitmap_init(dev))) {
+		DRM_ERROR("Cannot allocate memory for context bitmap.\n");
+		drm_proc_cleanup();
+		misc_deregister(&mga_misc);
+		mga_takedown(dev);
+		return retcode;
+	}
+
+	DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
+		 MGA_NAME,
+		 MGA_MAJOR,
+		 MGA_MINOR,
+		 MGA_PATCHLEVEL,
+		 MGA_DATE,
+		 mga_misc.minor);
+
+	return 0;
+}
+
+/* mga_cleanup is called via cleanup_module at module unload time. */
+
+static void __exit mga_cleanup(void)
+{
+	drm_device_t	      *dev = &mga_device;
+
+	DRM_DEBUG("\n");
+
+	drm_proc_cleanup();
+	if (misc_deregister(&mga_misc)) {
+		DRM_ERROR("Cannot unload module\n");
+	} else {
+		DRM_INFO("Module unloaded\n");
+	}
+	drm_ctxbitmap_cleanup(dev);
+#ifdef CONFIG_MTRR
+   	if(dev->agp && dev->agp->agp_mtrr) {
+	   	int retval;
+	   	retval = mtrr_del(dev->agp->agp_mtrr,
+				  dev->agp->agp_info.aper_base,
+				  dev->agp->agp_info.aper_size * 1024*1024);
+	   	DRM_DEBUG("mtrr_del = %d\n", retval);
+	}
+#endif
+
+	mga_takedown(dev);
+	if (dev->agp) {
+		drm_agp_uninit();
+		drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS);
+		dev->agp = NULL;
+	}
+}
+
+module_init(mga_init);
+module_exit(mga_cleanup);
+
+
+int mga_version(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_version_t version;
+	int	      len;
+
+	if (copy_from_user(&version,
+			   (drm_version_t *)arg,
+			   sizeof(version)))
+		return -EFAULT;
+
+#define DRM_COPY(name,value)				     \
+	len = strlen(value);				     \
+	if (len > name##_len) len = name##_len;		     \
+	name##_len = strlen(value);			     \
+	if (len && name) {				     \
+		if (copy_to_user(name, value, len))	     \
+			return -EFAULT;			     \
+	}
+
+	version.version_major	   = MGA_MAJOR;
+	version.version_minor	   = MGA_MINOR;
+	version.version_patchlevel = MGA_PATCHLEVEL;
+
+	DRM_COPY(version.name, MGA_NAME);
+	DRM_COPY(version.date, MGA_DATE);
+	DRM_COPY(version.desc, MGA_DESC);
+
+	if (copy_to_user((drm_version_t *)arg,
+			 &version,
+			 sizeof(version)))
+		return -EFAULT;
+	return 0;
+}
+
+int mga_open(struct inode *inode, struct file *filp)
+{
+	drm_device_t  *dev    = &mga_device;
+	int	      retcode = 0;
+
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	if (!(retcode = drm_open_helper(inode, filp, dev))) {
+#if LINUX_VERSION_CODE < 0x020333
+		MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+		atomic_inc(&dev->total_open);
+		spin_lock(&dev->count_lock);
+		if (!dev->open_count++) {
+			spin_unlock(&dev->count_lock);
+			return mga_setup(dev);
+		}
+		spin_unlock(&dev->count_lock);
+	}
+	return retcode;
+}
+
+int mga_release(struct inode *inode, struct file *filp)
+{
+	drm_file_t    *priv   = filp->private_data;
+	drm_device_t  *dev;
+	int	      retcode = 0;
+
+	lock_kernel();
+	dev    = priv->dev;
+	DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n",
+		  current->pid, dev->device, dev->open_count);
+
+	if (dev->lock.hw_lock && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)
+	    && dev->lock.pid == current->pid) {
+	      	mga_reclaim_buffers(dev, priv->pid);
+		DRM_INFO("Process %d dead (ctx %d, d_s = 0x%02lx)\n",
+			 current->pid,
+			 _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock),
+			 dev->dev_private ?
+			 ((drm_mga_private_t *)dev->dev_private)
+			 ->dispatch_status
+			 : 0);
+
+		if (dev->dev_private)
+			((drm_mga_private_t *)dev->dev_private)
+				->dispatch_status &= MGA_IN_DISPATCH;
+		
+		drm_lock_free(dev,
+			      &dev->lock.hw_lock->lock,
+			      _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+	} else if (dev->lock.hw_lock) {
+	   	/* The lock is required to reclaim buffers */
+	   	DECLARE_WAITQUEUE(entry, current);
+	   	add_wait_queue(&dev->lock.lock_queue, &entry);
+		for (;;) {
+			current->state = TASK_INTERRUPTIBLE;
+			if (!dev->lock.hw_lock) {
+				/* Device has been unregistered */
+				retcode = -EINTR;
+				break;
+			}
+			if (drm_lock_take(&dev->lock.hw_lock->lock,
+					  DRM_KERNEL_CONTEXT)) {
+				dev->lock.pid	    = priv->pid;
+				dev->lock.lock_time = jiffies;
+				atomic_inc(&dev->total_locks);
+				break;	/* Got lock */
+			}
+				/* Contention */
+			atomic_inc(&dev->total_sleeps);
+			schedule();
+			if (signal_pending(current)) {
+				retcode = -ERESTARTSYS;
+				break;
+			}
+		}
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&dev->lock.lock_queue, &entry);
+	   	if(!retcode) {
+		   	mga_reclaim_buffers(dev, priv->pid);
+			if (dev->dev_private)
+				((drm_mga_private_t *)dev->dev_private)
+					->dispatch_status &= MGA_IN_DISPATCH;
+		   	drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				      DRM_KERNEL_CONTEXT);
+		}
+	}
+	drm_fasync(-1, filp, 0);
+
+	down(&dev->struct_sem);
+	if (priv->remove_auth_on_close == 1) {
+		drm_file_t *temp = dev->file_first;
+		while(temp) {
+			temp->authenticated = 0;
+			temp = temp->next;
+		}
+	}
+	if (priv->prev) priv->prev->next = priv->next;
+	else		dev->file_first	 = priv->next;
+	if (priv->next) priv->next->prev = priv->prev;
+	else		dev->file_last	 = priv->prev;
+	up(&dev->struct_sem);
+
+	drm_free(priv, sizeof(*priv), DRM_MEM_FILES);
+#if LINUX_VERSION_CODE < 0x020333
+	MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+   	atomic_inc(&dev->total_close);
+   	spin_lock(&dev->count_lock);
+   	if (!--dev->open_count) {
+	   	if (atomic_read(&dev->ioctl_count) || dev->blocked) {
+		   	DRM_ERROR("Device busy: %d %d\n",
+				  atomic_read(&dev->ioctl_count),
+				  dev->blocked);
+		   	spin_unlock(&dev->count_lock);
+			unlock_kernel();
+		   	return -EBUSY;
+		}
+	   	spin_unlock(&dev->count_lock);
+		unlock_kernel();
+	   	return mga_takedown(dev);
+	}
+	spin_unlock(&dev->count_lock);
+	unlock_kernel();
+	return retcode;
+}
+
+
+/* drm_ioctl is called whenever a process performs an ioctl on /dev/drm. */
+
+int mga_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	int		 nr	 = DRM_IOCTL_NR(cmd);
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	int		 retcode = 0;
+	drm_ioctl_desc_t *ioctl;
+	drm_ioctl_t	 *func;
+
+	atomic_inc(&dev->ioctl_count);
+	atomic_inc(&dev->total_ioctl);
+	++priv->ioctl_count;
+
+	if (nr >= MGA_IOCTL_COUNT) {
+		retcode = -EINVAL;
+	} else {
+		ioctl	  = &mga_ioctls[nr];
+		func	  = ioctl->func;
+
+		if (!func) {
+			DRM_DEBUG("no function: pid = %d, cmd = 0x%02x,"
+				  " nr = 0x%02x, dev 0x%x, auth = %d\n",
+				  current->pid, cmd, nr, dev->device,
+				  priv->authenticated);
+			retcode = -EINVAL;
+		} else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN))
+			    || (ioctl->auth_needed && !priv->authenticated)) {
+			retcode = -EACCES;
+		} else {
+			retcode = (func)(inode, filp, cmd, arg);
+		}
+	}
+
+	atomic_dec(&dev->ioctl_count);
+	return retcode;
+}
+
+int mga_unlock(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	drm_lock_t	  lock;
+
+	if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)))
+		return -EFAULT;
+
+	if (lock.context == DRM_KERNEL_CONTEXT) {
+		DRM_ERROR("Process %d using kernel context %d\n",
+			  current->pid, lock.context);
+		return -EINVAL;
+	}
+
+	atomic_inc(&dev->total_unlocks);
+	if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock))
+		atomic_inc(&dev->total_contends);
+	drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT);
+	mga_dma_schedule(dev, 1);
+
+	if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+			  DRM_KERNEL_CONTEXT)) DRM_ERROR("\n");
+
+	unblock_all_signals();
+	return 0;
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/mga_drv.h linux/drivers/char/drm-4.0/mga_drv.h
--- linux-2.4.18-pre2/drivers/char/drm-4.0/mga_drv.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/mga_drv.h	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,520 @@
+/* mga_drv.h -- Private header for the Matrox g200/g400 driver -*- linux-c -*-
+ * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
+ * 	    Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#ifndef _MGA_DRV_H_
+#define _MGA_DRV_H_
+
+#define MGA_BUF_IN_USE         0
+#define MGA_BUF_SWAP_PENDING   1
+#define MGA_BUF_FORCE_FIRE     2
+#define MGA_BUF_NEEDS_OVERFLOW 3
+
+typedef struct {
+	long buffer_status; /* long req'd for set_bit() --RR */
+   	int num_dwords;
+   	int max_dwords;
+   	u32 *current_dma_ptr;
+   	u32 *head;
+   	u32 phys_head;
+	unsigned int prim_age;
+   	int sec_used;
+   	int idx;
+} drm_mga_prim_buf_t;
+
+typedef struct _drm_mga_freelist {
+   	__volatile__ unsigned int age;
+   	drm_buf_t *buf;
+   	struct _drm_mga_freelist *next;
+   	struct _drm_mga_freelist *prev;
+} drm_mga_freelist_t;
+
+#define MGA_IN_DISPATCH   0
+#define MGA_IN_FLUSH      1
+#define MGA_IN_WAIT       2
+#define MGA_IN_GETBUF	  3
+
+typedef struct _drm_mga_private {
+   	long dispatch_status;  /* long req'd for set_bit() --RR */
+	unsigned int next_prim_age;
+	__volatile__ unsigned int last_prim_age;
+   	int reserved_map_idx;
+   	int buffer_map_idx;
+   	drm_mga_sarea_t *sarea_priv;
+   	int primary_size;
+   	int warp_ucode_size;
+   	int chipset;
+   	unsigned int frontOffset;
+   	unsigned int backOffset;
+   	unsigned int depthOffset;
+   	unsigned int textureOffset;
+   	unsigned int textureSize;
+   	int cpp;
+   	unsigned int stride;
+   	int sgram;
+	int use_agp;
+   	drm_mga_warp_index_t WarpIndex[MGA_MAX_G400_PIPES];
+	unsigned int WarpPipe;
+	unsigned int vertexsize;
+   	atomic_t pending_bufs;
+   	void *status_page;
+   	unsigned long real_status_page;
+   	u8 *ioremap;
+   	drm_mga_prim_buf_t **prim_bufs;
+   	drm_mga_prim_buf_t *next_prim;
+   	drm_mga_prim_buf_t *last_prim;
+   	drm_mga_prim_buf_t *current_prim;
+   	int current_prim_idx;
+   	drm_mga_freelist_t *head;
+   	drm_mga_freelist_t *tail;
+   	wait_queue_head_t flush_queue;	/* Processes waiting until flush    */
+      	wait_queue_head_t wait_queue;	/* Processes waiting until interrupt */
+	wait_queue_head_t buf_queue;    /* Processes waiting for a free buf */
+	/* Some validated register values:
+	 */
+	u32 mAccess;
+} drm_mga_private_t;
+
+				/* mga_drv.c */
+extern int  mga_version(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  mga_open(struct inode *inode, struct file *filp);
+extern int  mga_release(struct inode *inode, struct file *filp);
+extern int  mga_ioctl(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  mga_unlock(struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg);
+
+				/* mga_dma.c */
+extern int  mga_dma_schedule(drm_device_t *dev, int locked);
+extern int  mga_dma(struct inode *inode, struct file *filp,
+		      unsigned int cmd, unsigned long arg);
+extern int  mga_irq_install(drm_device_t *dev, int irq);
+extern int  mga_irq_uninstall(drm_device_t *dev);
+extern int  mga_control(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  mga_lock(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+
+/* mga_dma_init does init and release */
+extern int mga_dma_init(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int mga_dma_cleanup(drm_device_t *dev);
+extern int mga_flush_ioctl(struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg);
+extern unsigned int mga_create_sync_tag(drm_device_t *dev);
+extern drm_buf_t *mga_freelist_get(drm_device_t *dev);
+extern int mga_freelist_put(drm_device_t *dev, drm_buf_t *buf);
+extern int mga_advance_primary(drm_device_t *dev);
+extern void mga_reclaim_buffers(drm_device_t *dev, pid_t pid);
+
+
+				/* mga_bufs.c */
+extern int  mga_addbufs(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  mga_infobufs(struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg);
+extern int  mga_markbufs(struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg);
+extern int  mga_freebufs(struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg);
+extern int  mga_mapbufs(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  mga_addmap(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+				/* mga_state.c */
+extern int  mga_clear_bufs(struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg);
+extern int  mga_swap_bufs(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  mga_iload(struct inode *inode, struct file *filp,
+		      unsigned int cmd, unsigned long arg);
+extern int  mga_vertex(struct inode *inode, struct file *filp,
+		      unsigned int cmd, unsigned long arg);
+extern int  mga_indices(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+				/* mga_context.c */
+extern int  mga_resctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  mga_addctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  mga_modctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  mga_getctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  mga_switchctx(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  mga_newctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  mga_rmctx(struct inode *inode, struct file *filp,
+		      unsigned int cmd, unsigned long arg);
+
+extern int  mga_context_switch(drm_device_t *dev, int old, int new);
+extern int  mga_context_switch_complete(drm_device_t *dev, int new);
+
+#define mga_flush_write_combine()	mb()
+
+typedef enum {
+	TT_GENERAL,
+	TT_BLIT,
+	TT_VECTOR,
+	TT_VERTEX
+} transferType_t;
+
+typedef struct {
+   	drm_mga_freelist_t *my_freelist;
+	int discard;
+	int dispatched;
+} drm_mga_buf_priv_t;
+
+#define DWGREG0 	0x1c00
+#define DWGREG0_END 	0x1dff
+#define DWGREG1		0x2c00
+#define DWGREG1_END	0x2dff
+
+#define ISREG0(r)	(r >= DWGREG0 && r <= DWGREG0_END)
+#define ADRINDEX0(r)	(u8)((r - DWGREG0) >> 2)
+#define ADRINDEX1(r)	(u8)(((r - DWGREG1) >> 2) | 0x80)
+#define ADRINDEX(r)	(ISREG0(r) ? ADRINDEX0(r) : ADRINDEX1(r))
+
+#define MGA_VERBOSE 0
+#define MGA_NUM_PRIM_BUFS 	8
+
+#define PRIMLOCALS	u8 tempIndex[4]; u32 *dma_ptr; u32 phys_head; \
+			int outcount, num_dwords
+
+#define PRIM_OVERFLOW(dev, dev_priv, length) do {			   \
+	drm_mga_prim_buf_t *tmp_buf =					   \
+ 		dev_priv->prim_bufs[dev_priv->current_prim_idx];	   \
+	if( test_bit(MGA_BUF_NEEDS_OVERFLOW, &tmp_buf->buffer_status)) {   \
+ 		mga_advance_primary(dev);				   \
+ 		mga_dma_schedule(dev, 1);				   \
+		tmp_buf = dev_priv->prim_bufs[dev_priv->current_prim_idx]; \
+ 	} else if( tmp_buf->max_dwords - tmp_buf->num_dwords < length ||   \
+ 	           tmp_buf->sec_used > MGA_DMA_BUF_NR/2) {		   \
+		set_bit(MGA_BUF_FORCE_FIRE, &tmp_buf->buffer_status);	   \
+ 		mga_advance_primary(dev);				   \
+ 		mga_dma_schedule(dev, 1);				   \
+		tmp_buf = dev_priv->prim_bufs[dev_priv->current_prim_idx]; \
+	}								   \
+	if(MGA_VERBOSE)							   \
+		DRM_DEBUG("PRIMGETPTR in %s\n", __FUNCTION__);		   \
+	dma_ptr = tmp_buf->current_dma_ptr;				   \
+	num_dwords = tmp_buf->num_dwords;				   \
+	phys_head = tmp_buf->phys_head;					   \
+	outcount = 0;							   \
+} while(0)
+
+#define PRIMGETPTR(dev_priv) do {					\
+	drm_mga_prim_buf_t *tmp_buf =					\
+		dev_priv->prim_bufs[dev_priv->current_prim_idx];	\
+	if(MGA_VERBOSE)							\
+		DRM_DEBUG("PRIMGETPTR in %s\n", __FUNCTION__);		\
+	dma_ptr = tmp_buf->current_dma_ptr;				\
+	num_dwords = tmp_buf->num_dwords;				\
+	phys_head = tmp_buf->phys_head;					\
+	outcount = 0;							\
+} while(0)
+
+#define PRIMPTR(prim_buf) do {					\
+	if(MGA_VERBOSE)						\
+		DRM_DEBUG("PRIMPTR in %s\n", __FUNCTION__);	\
+	dma_ptr = prim_buf->current_dma_ptr;			\
+	num_dwords = prim_buf->num_dwords;			\
+	phys_head = prim_buf->phys_head;			\
+	outcount = 0;						\
+} while(0)
+
+#define PRIMFINISH(prim_buf) do {				\
+	if (MGA_VERBOSE) {					\
+		DRM_DEBUG( "PRIMFINISH in %s\n", __FUNCTION__);	\
+                if (outcount & 3)				\
+                      DRM_DEBUG(" --- truncation\n");	        \
+        }							\
+	prim_buf->num_dwords = num_dwords;			\
+	prim_buf->current_dma_ptr = dma_ptr;			\
+} while(0)
+
+#define PRIMADVANCE(dev_priv)	do {				\
+drm_mga_prim_buf_t *tmp_buf = 					\
+	dev_priv->prim_bufs[dev_priv->current_prim_idx];	\
+	if (MGA_VERBOSE) {					\
+		DRM_DEBUG("PRIMADVANCE in %s\n", __FUNCTION__);	\
+                if (outcount & 3)				\
+                      DRM_DEBUG(" --- truncation\n");	\
+        }							\
+	tmp_buf->num_dwords = num_dwords;      			\
+	tmp_buf->current_dma_ptr = dma_ptr;    			\
+} while (0)
+
+#define PRIMUPDATE(dev_priv)	do {					\
+	drm_mga_prim_buf_t *tmp_buf =					\
+		dev_priv->prim_bufs[dev_priv->current_prim_idx];	\
+	tmp_buf->sec_used++;						\
+} while (0)
+
+#define AGEBUF(dev_priv, buf_priv)	do {				\
+	drm_mga_prim_buf_t *tmp_buf =					\
+		dev_priv->prim_bufs[dev_priv->current_prim_idx];	\
+	buf_priv->my_freelist->age = tmp_buf->prim_age;			\
+} while (0)
+
+
+#define PRIMOUTREG(reg, val) do {					\
+	tempIndex[outcount]=ADRINDEX(reg);				\
+	dma_ptr[1+outcount] = val;					\
+	if (MGA_VERBOSE)						\
+		DRM_DEBUG("   PRIMOUT %d: 0x%x -- 0x%x\n",		\
+		       num_dwords + 1 + outcount, ADRINDEX(reg), val);	\
+	if( ++outcount == 4) {						\
+		outcount = 0;						\
+		dma_ptr[0] = *(unsigned long *)tempIndex;		\
+		dma_ptr+=5;						\
+		num_dwords += 5;					\
+	}								\
+}while (0)
+
+/* A reduced set of the mga registers.
+ */
+
+#define MGAREG_MGA_EXEC 			0x0100
+#define MGAREG_ALPHACTRL 			0x2c7c
+#define MGAREG_AR0 				0x1c60
+#define MGAREG_AR1 				0x1c64
+#define MGAREG_AR2 				0x1c68
+#define MGAREG_AR3 				0x1c6c
+#define MGAREG_AR4 				0x1c70
+#define MGAREG_AR5 				0x1c74
+#define MGAREG_AR6 				0x1c78
+#define MGAREG_CXBNDRY				0x1c80
+#define MGAREG_CXLEFT 				0x1ca0
+#define MGAREG_CXRIGHT				0x1ca4
+#define MGAREG_DMAPAD 				0x1c54
+#define MGAREG_DSTORG 				0x2cb8
+#define MGAREG_DWGCTL 				0x1c00
+#define MGAREG_DWGSYNC				0x2c4c
+#define MGAREG_FCOL 				0x1c24
+#define MGAREG_FIFOSTATUS 			0x1e10
+#define MGAREG_FOGCOL 				0x1cf4
+#define MGAREG_FXBNDRY				0x1c84
+#define MGAREG_FXLEFT 				0x1ca8
+#define MGAREG_FXRIGHT				0x1cac
+#define MGAREG_ICLEAR 				0x1e18
+#define MGAREG_IEN 				0x1e1c
+#define MGAREG_LEN 				0x1c5c
+#define MGAREG_MACCESS				0x1c04
+#define MGAREG_PITCH 				0x1c8c
+#define MGAREG_PLNWT 				0x1c1c
+#define MGAREG_PRIMADDRESS 			0x1e58
+#define MGAREG_PRIMEND				0x1e5c
+#define MGAREG_PRIMPTR				0x1e50
+#define MGAREG_SECADDRESS 			0x2c40
+#define MGAREG_SECEND 				0x2c44
+#define MGAREG_SETUPADDRESS 			0x2cd0
+#define MGAREG_SETUPEND 			0x2cd4
+#define MGAREG_SOFTRAP				0x2c48
+#define MGAREG_SRCORG 				0x2cb4
+#define MGAREG_STATUS 				0x1e14
+#define MGAREG_STENCIL				0x2cc8
+#define MGAREG_STENCILCTL 			0x2ccc
+#define MGAREG_TDUALSTAGE0 			0x2cf8
+#define MGAREG_TDUALSTAGE1 			0x2cfc
+#define MGAREG_TEXBORDERCOL 			0x2c5c
+#define MGAREG_TEXCTL 				0x2c30
+#define MGAREG_TEXCTL2				0x2c3c
+#define MGAREG_TEXFILTER 			0x2c58
+#define MGAREG_TEXHEIGHT 			0x2c2c
+#define MGAREG_TEXORG 				0x2c24
+#define MGAREG_TEXORG1				0x2ca4
+#define MGAREG_TEXORG2				0x2ca8
+#define MGAREG_TEXORG3				0x2cac
+#define MGAREG_TEXORG4				0x2cb0
+#define MGAREG_TEXTRANS 			0x2c34
+#define MGAREG_TEXTRANSHIGH 			0x2c38
+#define MGAREG_TEXWIDTH 			0x2c28
+#define MGAREG_WACCEPTSEQ 			0x1dd4
+#define MGAREG_WCODEADDR 			0x1e6c
+#define MGAREG_WFLAG 				0x1dc4
+#define MGAREG_WFLAG1 				0x1de0
+#define MGAREG_WFLAGNB				0x1e64
+#define MGAREG_WFLAGNB1 			0x1e08
+#define MGAREG_WGETMSB				0x1dc8
+#define MGAREG_WIADDR 				0x1dc0
+#define MGAREG_WIADDR2				0x1dd8
+#define MGAREG_WMISC 				0x1e70
+#define MGAREG_WVRTXSZ				0x1dcc
+#define MGAREG_YBOT 				0x1c9c
+#define MGAREG_YDST 				0x1c90
+#define MGAREG_YDSTLEN				0x1c88
+#define MGAREG_YDSTORG				0x1c94
+#define MGAREG_YTOP 				0x1c98
+#define MGAREG_ZORG 				0x1c0c
+
+/* Warp registers */
+#define MGAREG_WR0                              0x2d00
+#define MGAREG_WR1                              0x2d04
+#define MGAREG_WR2                              0x2d08
+#define MGAREG_WR3                              0x2d0c
+#define MGAREG_WR4                              0x2d10
+#define MGAREG_WR5                              0x2d14
+#define MGAREG_WR6                              0x2d18
+#define MGAREG_WR7                              0x2d1c
+#define MGAREG_WR8                              0x2d20
+#define MGAREG_WR9                              0x2d24
+#define MGAREG_WR10                             0x2d28
+#define MGAREG_WR11                             0x2d2c
+#define MGAREG_WR12                             0x2d30
+#define MGAREG_WR13                             0x2d34
+#define MGAREG_WR14                             0x2d38
+#define MGAREG_WR15                             0x2d3c
+#define MGAREG_WR16                             0x2d40
+#define MGAREG_WR17                             0x2d44
+#define MGAREG_WR18                             0x2d48
+#define MGAREG_WR19                             0x2d4c
+#define MGAREG_WR20                             0x2d50
+#define MGAREG_WR21                             0x2d54
+#define MGAREG_WR22                             0x2d58
+#define MGAREG_WR23                             0x2d5c
+#define MGAREG_WR24                             0x2d60
+#define MGAREG_WR25                             0x2d64
+#define MGAREG_WR26                             0x2d68
+#define MGAREG_WR27                             0x2d6c
+#define MGAREG_WR28                             0x2d70
+#define MGAREG_WR29                             0x2d74
+#define MGAREG_WR30                             0x2d78
+#define MGAREG_WR31                             0x2d7c
+#define MGAREG_WR32                             0x2d80
+#define MGAREG_WR33                             0x2d84
+#define MGAREG_WR34                             0x2d88
+#define MGAREG_WR35                             0x2d8c
+#define MGAREG_WR36                             0x2d90
+#define MGAREG_WR37                             0x2d94
+#define MGAREG_WR38                             0x2d98
+#define MGAREG_WR39                             0x2d9c
+#define MGAREG_WR40                             0x2da0
+#define MGAREG_WR41                             0x2da4
+#define MGAREG_WR42                             0x2da8
+#define MGAREG_WR43                             0x2dac
+#define MGAREG_WR44                             0x2db0
+#define MGAREG_WR45                             0x2db4
+#define MGAREG_WR46                             0x2db8
+#define MGAREG_WR47                             0x2dbc
+#define MGAREG_WR48                             0x2dc0
+#define MGAREG_WR49                             0x2dc4
+#define MGAREG_WR50                             0x2dc8
+#define MGAREG_WR51                             0x2dcc
+#define MGAREG_WR52                             0x2dd0
+#define MGAREG_WR53                             0x2dd4
+#define MGAREG_WR54                             0x2dd8
+#define MGAREG_WR55                             0x2ddc
+#define MGAREG_WR56                             0x2de0
+#define MGAREG_WR57                             0x2de4
+#define MGAREG_WR58                             0x2de8
+#define MGAREG_WR59                             0x2dec
+#define MGAREG_WR60                             0x2df0
+#define MGAREG_WR61                             0x2df4
+#define MGAREG_WR62                             0x2df8
+#define MGAREG_WR63                             0x2dfc
+
+#define PDEA_pagpxfer_enable			0x2
+
+#define WIA_wmode_suspend			0x0
+#define WIA_wmode_start 			0x3
+#define WIA_wagp_agp				0x4
+
+#define DC_opcod_line_open 			0x0
+#define DC_opcod_autoline_open 			0x1
+#define DC_opcod_line_close 			0x2
+#define DC_opcod_autoline_close 		0x3
+#define DC_opcod_trap 				0x4
+#define DC_opcod_texture_trap 			0x6
+#define DC_opcod_bitblt 			0x8
+#define DC_opcod_iload 				0x9
+#define DC_atype_rpl 				0x0
+#define DC_atype_rstr 				0x10
+#define DC_atype_zi 				0x30
+#define DC_atype_blk 				0x40
+#define DC_atype_i 				0x70
+#define DC_linear_xy 				0x0
+#define DC_linear_linear 			0x80
+#define DC_zmode_nozcmp 			0x0
+#define DC_zmode_ze 				0x200
+#define DC_zmode_zne 				0x300
+#define DC_zmode_zlt 				0x400
+#define DC_zmode_zlte 				0x500
+#define DC_zmode_zgt 				0x600
+#define DC_zmode_zgte 				0x700
+#define DC_solid_disable 			0x0
+#define DC_solid_enable 			0x800
+#define DC_arzero_disable 			0x0
+#define DC_arzero_enable 			0x1000
+#define DC_sgnzero_disable 			0x0
+#define DC_sgnzero_enable 			0x2000
+#define DC_shftzero_disable 			0x0
+#define DC_shftzero_enable 			0x4000
+#define DC_bop_SHIFT 				16
+#define DC_trans_SHIFT 				20
+#define DC_bltmod_bmonolef 			0x0
+#define DC_bltmod_bmonowf 			0x8000000
+#define DC_bltmod_bplan 			0x2000000
+#define DC_bltmod_bfcol 			0x4000000
+#define DC_bltmod_bu32bgr 			0x6000000
+#define DC_bltmod_bu32rgb 			0xe000000
+#define DC_bltmod_bu24bgr 			0x16000000
+#define DC_bltmod_bu24rgb 			0x1e000000
+#define DC_pattern_disable 			0x0
+#define DC_pattern_enable 			0x20000000
+#define DC_transc_disable 			0x0
+#define DC_transc_enable 			0x40000000
+#define DC_clipdis_disable 			0x0
+#define DC_clipdis_enable 			0x80000000
+
+
+#define SETADD_mode_vertlist                   	0x0
+
+
+#define MGA_CLEAR_CMD (DC_opcod_trap | DC_arzero_enable | 		\
+		       DC_sgnzero_enable | DC_shftzero_enable | 	\
+		       (0xC << DC_bop_SHIFT) | DC_clipdis_enable | 	\
+		       DC_solid_enable | DC_transc_enable)
+
+
+#define MGA_COPY_CMD (DC_opcod_bitblt | DC_atype_rpl | DC_linear_xy |	\
+		      DC_solid_disable | DC_arzero_disable | 		\
+		      DC_sgnzero_enable | DC_shftzero_enable | 		\
+		      (0xC << DC_bop_SHIFT) | DC_bltmod_bfcol | 	\
+		      DC_pattern_disable | DC_transc_disable | 		\
+		      DC_clipdis_enable)				\
+
+#define MGA_FLUSH_CMD (DC_opcod_texture_trap | (0xF << DC_trans_SHIFT) |\
+		       DC_arzero_enable | DC_sgnzero_enable |		\
+		       DC_atype_i)
+
+#endif
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/mga_state.c linux/drivers/char/drm-4.0/mga_state.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/mga_state.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/mga_state.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,1017 @@
+/* mga_state.c -- State support for mga g200/g400 -*- linux-c -*-
+ * Created: Thu Jan 27 02:53:43 2000 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Jeff Hartmann <jhartmann@valinux.com>
+ * 	    Keith Whitwell <keithw@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "mga_drv.h"
+#include "drm.h"
+
+/* If you change the functions to set state, PLEASE
+ * change these values
+ */
+
+#define MGAEMITCLIP_SIZE	10
+#define MGAEMITCTX_SIZE		20
+#define MGAG200EMITTEX_SIZE 	20
+#define MGAG400EMITTEX0_SIZE	30
+#define MGAG400EMITTEX1_SIZE	25
+#define MGAG400EMITPIPE_SIZE	50
+#define MGAG200EMITPIPE_SIZE	15
+
+#define MAX_STATE_SIZE ((MGAEMITCLIP_SIZE * MGA_NR_SAREA_CLIPRECTS) + \
+			MGAEMITCTX_SIZE + MGAG400EMITTEX0_SIZE + \
+			MGAG400EMITTEX1_SIZE + MGAG400EMITPIPE_SIZE)
+
+static void mgaEmitClipRect(drm_mga_private_t * dev_priv,
+			    drm_clip_rect_t * box)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int *regs = sarea_priv->ContextState;
+	PRIMLOCALS;
+
+	/* This takes 10 dwords */
+	PRIMGETPTR(dev_priv);
+
+	/* Force reset of dwgctl on G400 (eliminates clip disable bit) */
+	if (dev_priv->chipset == MGA_CARD_TYPE_G400) {
+#if 0
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+		PRIMOUTREG(MGAREG_DWGSYNC, 0);
+		PRIMOUTREG(MGAREG_DWGSYNC, 0);
+		PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]);
+#else
+		PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]);
+		PRIMOUTREG(MGAREG_LEN + MGAREG_MGA_EXEC, 0x80000000);
+		PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]);
+		PRIMOUTREG(MGAREG_LEN + MGAREG_MGA_EXEC, 0x80000000);
+#endif
+	}
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_CXBNDRY, ((box->x2) << 16) | (box->x1));
+	PRIMOUTREG(MGAREG_YTOP, box->y1 * dev_priv->stride / dev_priv->cpp);
+	PRIMOUTREG(MGAREG_YBOT, box->y2 * dev_priv->stride / dev_priv->cpp);
+
+	PRIMADVANCE(dev_priv);
+}
+
+static void mgaEmitContext(drm_mga_private_t * dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int *regs = sarea_priv->ContextState;
+	PRIMLOCALS;
+
+	/* This takes a max of 20 dwords */
+	PRIMGETPTR(dev_priv);
+
+	PRIMOUTREG(MGAREG_DSTORG, regs[MGA_CTXREG_DSTORG]);
+	PRIMOUTREG(MGAREG_MACCESS, regs[MGA_CTXREG_MACCESS]);
+	PRIMOUTREG(MGAREG_PLNWT, regs[MGA_CTXREG_PLNWT]);
+	PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]);
+
+	PRIMOUTREG(MGAREG_ALPHACTRL, regs[MGA_CTXREG_ALPHACTRL]);
+	PRIMOUTREG(MGAREG_FOGCOL, regs[MGA_CTXREG_FOGCOLOR]);
+	PRIMOUTREG(MGAREG_WFLAG, regs[MGA_CTXREG_WFLAG]);
+	PRIMOUTREG(MGAREG_ZORG, dev_priv->depthOffset);	/* invarient */
+
+	if (dev_priv->chipset == MGA_CARD_TYPE_G400) {
+		PRIMOUTREG(MGAREG_WFLAG1, regs[MGA_CTXREG_WFLAG]);
+		PRIMOUTREG(MGAREG_TDUALSTAGE0, regs[MGA_CTXREG_TDUAL0]);
+		PRIMOUTREG(MGAREG_TDUALSTAGE1, regs[MGA_CTXREG_TDUAL1]);
+		PRIMOUTREG(MGAREG_FCOL, regs[MGA_CTXREG_FCOL]);
+
+		PRIMOUTREG(MGAREG_STENCIL, regs[MGA_CTXREG_STENCIL]);
+		PRIMOUTREG(MGAREG_STENCILCTL, regs[MGA_CTXREG_STENCILCTL]);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+	} else {
+		PRIMOUTREG(MGAREG_FCOL, regs[MGA_CTXREG_FCOL]);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+	}
+
+	PRIMADVANCE(dev_priv);
+}
+
+static void mgaG200EmitTex(drm_mga_private_t * dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int *regs = sarea_priv->TexState[0];
+	PRIMLOCALS;
+
+	PRIMGETPTR(dev_priv);
+
+	/* This takes 20 dwords */
+
+	PRIMOUTREG(MGAREG_TEXCTL2, regs[MGA_TEXREG_CTL2]);
+	PRIMOUTREG(MGAREG_TEXCTL, regs[MGA_TEXREG_CTL]);
+	PRIMOUTREG(MGAREG_TEXFILTER, regs[MGA_TEXREG_FILTER]);
+	PRIMOUTREG(MGAREG_TEXBORDERCOL, regs[MGA_TEXREG_BORDERCOL]);
+
+	PRIMOUTREG(MGAREG_TEXORG, regs[MGA_TEXREG_ORG]);
+	PRIMOUTREG(MGAREG_TEXORG1, regs[MGA_TEXREG_ORG1]);
+	PRIMOUTREG(MGAREG_TEXORG2, regs[MGA_TEXREG_ORG2]);
+	PRIMOUTREG(MGAREG_TEXORG3, regs[MGA_TEXREG_ORG3]);
+
+	PRIMOUTREG(MGAREG_TEXORG4, regs[MGA_TEXREG_ORG4]);
+	PRIMOUTREG(MGAREG_TEXWIDTH, regs[MGA_TEXREG_WIDTH]);
+	PRIMOUTREG(MGAREG_TEXHEIGHT, regs[MGA_TEXREG_HEIGHT]);
+	PRIMOUTREG(MGAREG_WR24, regs[MGA_TEXREG_WIDTH]);
+
+	PRIMOUTREG(MGAREG_WR34, regs[MGA_TEXREG_HEIGHT]);
+	PRIMOUTREG(MGAREG_TEXTRANS, 0xffff);
+	PRIMOUTREG(MGAREG_TEXTRANSHIGH, 0xffff);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+
+	PRIMADVANCE(dev_priv);
+}
+
+#define TMC_dualtex_enable 		0x80
+
+static void mgaG400EmitTex0(drm_mga_private_t * dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int *regs = sarea_priv->TexState[0];
+	PRIMLOCALS;
+
+	PRIMGETPTR(dev_priv);
+
+	/* This takes 30 dwords */
+
+	PRIMOUTREG(MGAREG_TEXCTL2, regs[MGA_TEXREG_CTL2] | 0x00008000);
+	PRIMOUTREG(MGAREG_TEXCTL, regs[MGA_TEXREG_CTL]);
+	PRIMOUTREG(MGAREG_TEXFILTER, regs[MGA_TEXREG_FILTER]);
+	PRIMOUTREG(MGAREG_TEXBORDERCOL, regs[MGA_TEXREG_BORDERCOL]);
+
+	PRIMOUTREG(MGAREG_TEXORG, regs[MGA_TEXREG_ORG]);
+	PRIMOUTREG(MGAREG_TEXORG1, regs[MGA_TEXREG_ORG1]);
+	PRIMOUTREG(MGAREG_TEXORG2, regs[MGA_TEXREG_ORG2]);
+	PRIMOUTREG(MGAREG_TEXORG3, regs[MGA_TEXREG_ORG3]);
+
+	PRIMOUTREG(MGAREG_TEXORG4, regs[MGA_TEXREG_ORG4]);
+	PRIMOUTREG(MGAREG_TEXWIDTH, regs[MGA_TEXREG_WIDTH]);
+	PRIMOUTREG(MGAREG_TEXHEIGHT, regs[MGA_TEXREG_HEIGHT]);
+	PRIMOUTREG(MGAREG_WR49, 0);
+
+	PRIMOUTREG(MGAREG_WR57, 0);
+	PRIMOUTREG(MGAREG_WR53, 0);
+	PRIMOUTREG(MGAREG_WR61, 0);
+	PRIMOUTREG(MGAREG_WR52, 0x40);
+
+	PRIMOUTREG(MGAREG_WR60, 0x40);
+	PRIMOUTREG(MGAREG_WR54, regs[MGA_TEXREG_WIDTH] | 0x40);
+	PRIMOUTREG(MGAREG_WR62, regs[MGA_TEXREG_HEIGHT] | 0x40);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_TEXTRANS, 0xffff);
+	PRIMOUTREG(MGAREG_TEXTRANSHIGH, 0xffff);
+
+	PRIMADVANCE(dev_priv);
+}
+
+#define TMC_map1_enable 		0x80000000
+
+static void mgaG400EmitTex1(drm_mga_private_t * dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int *regs = sarea_priv->TexState[1];
+	PRIMLOCALS;
+
+	PRIMGETPTR(dev_priv);
+
+	/* This takes 25 dwords */
+
+	PRIMOUTREG(MGAREG_TEXCTL2,
+		   regs[MGA_TEXREG_CTL2] | TMC_map1_enable | 0x00008000);
+	PRIMOUTREG(MGAREG_TEXCTL, regs[MGA_TEXREG_CTL]);
+	PRIMOUTREG(MGAREG_TEXFILTER, regs[MGA_TEXREG_FILTER]);
+	PRIMOUTREG(MGAREG_TEXBORDERCOL, regs[MGA_TEXREG_BORDERCOL]);
+
+	PRIMOUTREG(MGAREG_TEXORG, regs[MGA_TEXREG_ORG]);
+	PRIMOUTREG(MGAREG_TEXORG1, regs[MGA_TEXREG_ORG1]);
+	PRIMOUTREG(MGAREG_TEXORG2, regs[MGA_TEXREG_ORG2]);
+	PRIMOUTREG(MGAREG_TEXORG3, regs[MGA_TEXREG_ORG3]);
+
+	PRIMOUTREG(MGAREG_TEXORG4, regs[MGA_TEXREG_ORG4]);
+	PRIMOUTREG(MGAREG_TEXWIDTH, regs[MGA_TEXREG_WIDTH]);
+	PRIMOUTREG(MGAREG_TEXHEIGHT, regs[MGA_TEXREG_HEIGHT]);
+	PRIMOUTREG(MGAREG_WR49, 0);
+
+	PRIMOUTREG(MGAREG_WR57, 0);
+	PRIMOUTREG(MGAREG_WR53, 0);
+	PRIMOUTREG(MGAREG_WR61, 0);
+	PRIMOUTREG(MGAREG_WR52, regs[MGA_TEXREG_WIDTH] | 0x40);
+
+	PRIMOUTREG(MGAREG_WR60, regs[MGA_TEXREG_HEIGHT] | 0x40);
+	PRIMOUTREG(MGAREG_TEXTRANS, 0xffff);
+	PRIMOUTREG(MGAREG_TEXTRANSHIGH, 0xffff);
+	PRIMOUTREG(MGAREG_TEXCTL2, regs[MGA_TEXREG_CTL2] | 0x00008000);
+
+	PRIMADVANCE(dev_priv);
+}
+
+#define MAGIC_FPARAM_HEX_VALUE 0x46480000
+/* This is the hex value of 12800.0f which is a magic value we must
+ * set in wr56.
+ */
+
+static void mgaG400EmitPipe(drm_mga_private_t * dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int pipe = sarea_priv->WarpPipe;
+	PRIMLOCALS;
+
+	PRIMGETPTR(dev_priv);
+
+	/* This takes 50 dwords */
+
+	/* Establish vertex size.  
+	 */
+	PRIMOUTREG(MGAREG_WIADDR2, WIA_wmode_suspend);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+
+	if (pipe & MGA_T2) {
+		PRIMOUTREG(MGAREG_WVRTXSZ, 0x00001e09);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+
+		PRIMOUTREG(MGAREG_WACCEPTSEQ, 0);
+		PRIMOUTREG(MGAREG_WACCEPTSEQ, 0);
+		PRIMOUTREG(MGAREG_WACCEPTSEQ, 0);
+		PRIMOUTREG(MGAREG_WACCEPTSEQ, 0x1e000000);
+	} else {
+		if (dev_priv->WarpPipe & MGA_T2) {
+			/* Flush the WARP pipe */
+			PRIMOUTREG(MGAREG_YDST, 0);
+			PRIMOUTREG(MGAREG_FXLEFT, 0);
+			PRIMOUTREG(MGAREG_FXRIGHT, 1);
+			PRIMOUTREG(MGAREG_DWGCTL, MGA_FLUSH_CMD);
+
+			PRIMOUTREG(MGAREG_LEN + MGAREG_MGA_EXEC, 1);
+			PRIMOUTREG(MGAREG_DWGSYNC, 0x7000);
+			PRIMOUTREG(MGAREG_TEXCTL2, 0x00008000);
+			PRIMOUTREG(MGAREG_LEN + MGAREG_MGA_EXEC, 0);
+
+			PRIMOUTREG(MGAREG_TEXCTL2, 0x80 | 0x00008000);
+			PRIMOUTREG(MGAREG_LEN + MGAREG_MGA_EXEC, 0);
+			PRIMOUTREG(MGAREG_TEXCTL2, 0x00008000);
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+		}
+
+		PRIMOUTREG(MGAREG_WVRTXSZ, 0x00001807);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+
+		PRIMOUTREG(MGAREG_WACCEPTSEQ, 0);
+		PRIMOUTREG(MGAREG_WACCEPTSEQ, 0);
+		PRIMOUTREG(MGAREG_WACCEPTSEQ, 0);
+		PRIMOUTREG(MGAREG_WACCEPTSEQ, 0x18000000);
+	}
+
+	PRIMOUTREG(MGAREG_WFLAG, 0);
+	PRIMOUTREG(MGAREG_WFLAG1, 0);
+	PRIMOUTREG(MGAREG_WR56, MAGIC_FPARAM_HEX_VALUE);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+
+	PRIMOUTREG(MGAREG_WR49, 0);	/* Tex stage 0 */
+	PRIMOUTREG(MGAREG_WR57, 0);	/* Tex stage 0 */
+	PRIMOUTREG(MGAREG_WR53, 0);	/* Tex stage 1 */
+	PRIMOUTREG(MGAREG_WR61, 0);	/* Tex stage 1 */
+
+	PRIMOUTREG(MGAREG_WR54, 0x40);	/* Tex stage 0 : w */
+	PRIMOUTREG(MGAREG_WR62, 0x40);	/* Tex stage 0 : h */
+	PRIMOUTREG(MGAREG_WR52, 0x40);	/* Tex stage 1 : w */
+	PRIMOUTREG(MGAREG_WR60, 0x40);	/* Tex stage 1 : h */
+
+	/* Dma pading required due to hw bug */
+	PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff);
+	PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff);
+	PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff);
+	PRIMOUTREG(MGAREG_WIADDR2,
+		   (u32) (dev_priv->WarpIndex[pipe].
+			  phys_addr | WIA_wmode_start | WIA_wagp_agp));
+	PRIMADVANCE(dev_priv);
+}
+
+static void mgaG200EmitPipe(drm_mga_private_t * dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int pipe = sarea_priv->WarpPipe;
+	PRIMLOCALS;
+
+	PRIMGETPTR(dev_priv);
+
+	/* This takes 15 dwords */
+
+	PRIMOUTREG(MGAREG_WIADDR, WIA_wmode_suspend);
+	PRIMOUTREG(MGAREG_WVRTXSZ, 7);
+	PRIMOUTREG(MGAREG_WFLAG, 0);
+	PRIMOUTREG(MGAREG_WR24, 0);	/* tex w/h */
+
+	PRIMOUTREG(MGAREG_WR25, 0x100);
+	PRIMOUTREG(MGAREG_WR34, 0);	/* tex w/h */
+	PRIMOUTREG(MGAREG_WR42, 0xFFFF);
+	PRIMOUTREG(MGAREG_WR60, 0xFFFF);
+
+	/* Dma pading required due to hw bug */
+	PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff);
+	PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff);
+	PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff);
+	PRIMOUTREG(MGAREG_WIADDR,
+		   (u32) (dev_priv->WarpIndex[pipe].
+			  phys_addr | WIA_wmode_start | WIA_wagp_agp));
+
+	PRIMADVANCE( dev_priv );
+}
+
+static void mgaEmitState(drm_mga_private_t * dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int dirty = sarea_priv->dirty;
+
+	if (dev_priv->chipset == MGA_CARD_TYPE_G400) {
+		int multitex = sarea_priv->WarpPipe & MGA_T2;
+
+		if (sarea_priv->WarpPipe != dev_priv->WarpPipe) {
+			mgaG400EmitPipe(dev_priv);
+			dev_priv->WarpPipe = sarea_priv->WarpPipe;
+		}
+
+		if (dirty & MGA_UPLOAD_CTX) {
+			mgaEmitContext(dev_priv);
+			sarea_priv->dirty &= ~MGA_UPLOAD_CTX;
+		}
+
+		if (dirty & MGA_UPLOAD_TEX0) {
+			mgaG400EmitTex0(dev_priv);
+			sarea_priv->dirty &= ~MGA_UPLOAD_TEX0;
+		}
+
+		if ((dirty & MGA_UPLOAD_TEX1) && multitex) {
+			mgaG400EmitTex1(dev_priv);
+			sarea_priv->dirty &= ~MGA_UPLOAD_TEX1;
+		}
+	} else {
+		if (sarea_priv->WarpPipe != dev_priv->WarpPipe) {
+			mgaG200EmitPipe(dev_priv);
+			dev_priv->WarpPipe = sarea_priv->WarpPipe;
+		}
+
+		if (dirty & MGA_UPLOAD_CTX) {
+			mgaEmitContext(dev_priv);
+			sarea_priv->dirty &= ~MGA_UPLOAD_CTX;
+		}
+
+		if (dirty & MGA_UPLOAD_TEX0) {
+			mgaG200EmitTex(dev_priv);
+			sarea_priv->dirty &= ~MGA_UPLOAD_TEX0;
+		}
+	}
+}
+
+/* Disallow all write destinations except the front and backbuffer.
+ */
+static int mgaVerifyContext(drm_mga_private_t * dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int *regs = sarea_priv->ContextState;
+
+	if (regs[MGA_CTXREG_DSTORG] != dev_priv->frontOffset &&
+	    regs[MGA_CTXREG_DSTORG] != dev_priv->backOffset) {
+		DRM_DEBUG("BAD DSTORG: %x (front %x, back %x)\n\n",
+			  regs[MGA_CTXREG_DSTORG], dev_priv->frontOffset,
+			  dev_priv->backOffset);
+		regs[MGA_CTXREG_DSTORG] = 0;
+		return -1;
+	}
+
+	return 0;
+}
+
+/* Disallow texture reads from PCI space.
+ */
+static int mgaVerifyTex(drm_mga_private_t * dev_priv, int unit)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+
+	if ((sarea_priv->TexState[unit][MGA_TEXREG_ORG] & 0x3) == 0x1) {
+		DRM_DEBUG("BAD TEXREG_ORG: %x, unit %d\n",
+			  sarea_priv->TexState[unit][MGA_TEXREG_ORG],
+			  unit);
+		sarea_priv->TexState[unit][MGA_TEXREG_ORG] = 0;
+		return -1;
+	}
+
+	return 0;
+}
+
+static int mgaVerifyState(drm_mga_private_t * dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int dirty = sarea_priv->dirty;
+	int rv = 0;
+
+	if (sarea_priv->nbox > MGA_NR_SAREA_CLIPRECTS)
+		sarea_priv->nbox = MGA_NR_SAREA_CLIPRECTS;
+
+	if (dirty & MGA_UPLOAD_CTX)
+		rv |= mgaVerifyContext(dev_priv);
+
+	if (dirty & MGA_UPLOAD_TEX0)
+		rv |= mgaVerifyTex(dev_priv, 0);
+
+	if (dev_priv->chipset == MGA_CARD_TYPE_G400) {
+		if (dirty & MGA_UPLOAD_TEX1)
+			rv |= mgaVerifyTex(dev_priv, 1);
+
+		if (dirty & MGA_UPLOAD_PIPE)
+			rv |= (sarea_priv->WarpPipe > MGA_MAX_G400_PIPES);
+	} else {
+		if (dirty & MGA_UPLOAD_PIPE)
+			rv |= (sarea_priv->WarpPipe > MGA_MAX_G200_PIPES);
+	}
+
+	return rv == 0;
+}
+
+static int mgaVerifyIload(drm_mga_private_t * dev_priv,
+			  unsigned long bus_address,
+			  unsigned int dstOrg, int length)
+{
+	if (dstOrg < dev_priv->textureOffset ||
+	    dstOrg + length >
+	    (dev_priv->textureOffset + dev_priv->textureSize)) {
+		return -EINVAL;
+	}
+	if (length % 64) {
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/* This copies a 64 byte aligned agp region to the frambuffer
+ * with a standard blit, the ioctl needs to do checking */
+
+static void mga_dma_dispatch_tex_blit(drm_device_t * dev,
+				      unsigned long bus_address,
+				      int length, unsigned int destOrg)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	int use_agp = PDEA_pagpxfer_enable | 0x00000001;
+	u16 y2;
+	PRIMLOCALS;
+
+	y2 = length / 64;
+
+	PRIM_OVERFLOW(dev, dev_priv, 30);
+
+	PRIMOUTREG(MGAREG_DSTORG, destOrg);
+	PRIMOUTREG(MGAREG_MACCESS, 0x00000000);
+	PRIMOUTREG(MGAREG_SRCORG, (u32) bus_address | use_agp);
+	PRIMOUTREG(MGAREG_AR5, 64);
+
+	PRIMOUTREG(MGAREG_PITCH, 64);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DWGCTL, MGA_COPY_CMD);
+
+	PRIMOUTREG(MGAREG_AR0, 63);
+	PRIMOUTREG(MGAREG_AR3, 0);
+	PRIMOUTREG(MGAREG_FXBNDRY, (63 << 16));
+	PRIMOUTREG(MGAREG_YDSTLEN + MGAREG_MGA_EXEC, y2);
+
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_SRCORG, 0);
+	PRIMOUTREG(MGAREG_PITCH, dev_priv->stride / dev_priv->cpp);
+	PRIMOUTREG(MGAREG_DWGSYNC, 0x7000);
+	PRIMADVANCE(dev_priv);
+}
+
+static void mga_dma_dispatch_vertex(drm_device_t * dev, drm_buf_t * buf)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	drm_mga_buf_priv_t *buf_priv = buf->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned long address = (unsigned long) buf->bus_address;
+	int length = buf->used;
+	int use_agp = PDEA_pagpxfer_enable;
+	int i = 0;
+	PRIMLOCALS;
+
+	if (buf->used) {
+		/* WARNING: if you change any of the state functions verify
+		 * these numbers (Overestimating this doesn't hurt).
+		 */
+		buf_priv->dispatched = 1;
+		PRIM_OVERFLOW(dev, dev_priv,
+			      (MAX_STATE_SIZE + (5 * MGA_NR_SAREA_CLIPRECTS)));
+		mgaEmitState(dev_priv);
+
+#if 0
+		length = dev_priv->vertexsize * 3 * 4;
+#endif
+
+		do {
+			if (i < sarea_priv->nbox) {
+				mgaEmitClipRect(dev_priv,
+						&sarea_priv->boxes[i]);
+			}
+
+			PRIMGETPTR(dev_priv);
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_SECADDRESS,
+				   ((u32) address) | TT_VERTEX);
+			PRIMOUTREG(MGAREG_SECEND,
+				   (((u32) (address + length)) | use_agp));
+			PRIMADVANCE(dev_priv);
+		} while (++i < sarea_priv->nbox);
+	}
+	if (buf_priv->discard) {
+		if (buf_priv->dispatched == 1)
+			AGEBUF(dev_priv, buf_priv);
+		buf_priv->dispatched = 0;
+		mga_freelist_put(dev, buf);
+	}
+
+
+}
+
+
+static void mga_dma_dispatch_indices(drm_device_t * dev,
+				     drm_buf_t * buf,
+				     unsigned int start, unsigned int end)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	drm_mga_buf_priv_t *buf_priv = buf->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int address = (unsigned int) buf->bus_address;
+	int use_agp = PDEA_pagpxfer_enable;
+	int i = 0;
+	PRIMLOCALS;
+
+	if (start != end) {
+		/* WARNING: if you change any of the state functions verify
+		 * these numbers (Overestimating this doesn't hurt).
+		 */
+		buf_priv->dispatched = 1;
+		PRIM_OVERFLOW(dev, dev_priv,
+			      (MAX_STATE_SIZE + (5 * MGA_NR_SAREA_CLIPRECTS)));
+		mgaEmitState(dev_priv);
+
+		do {
+			if (i < sarea_priv->nbox) {
+				mgaEmitClipRect(dev_priv,
+						&sarea_priv->boxes[i]);
+			}
+
+			PRIMGETPTR(dev_priv);
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_SETUPADDRESS,
+				   ((address + start) |
+				    SETADD_mode_vertlist));
+			PRIMOUTREG(MGAREG_SETUPEND,
+				   ((address + end) | use_agp));
+/*  				   ((address + start + 12) | use_agp)); */
+			PRIMADVANCE(dev_priv);
+		} while (++i < sarea_priv->nbox);
+	}
+	if (buf_priv->discard) {
+		if (buf_priv->dispatched == 1)
+			AGEBUF(dev_priv, buf_priv);
+		buf_priv->dispatched = 0;
+		mga_freelist_put(dev, buf);
+	}
+}
+
+
+static void mga_dma_dispatch_clear(drm_device_t * dev, int flags,
+				   unsigned int clear_color,
+				   unsigned int clear_zval,
+				   unsigned int clear_colormask,
+				   unsigned int clear_depthmask)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int *regs = sarea_priv->ContextState;
+	int nbox = sarea_priv->nbox;
+	drm_clip_rect_t *pbox = sarea_priv->boxes;
+	unsigned int cmd;
+	int i;
+	PRIMLOCALS;
+
+	if (dev_priv->sgram)
+		cmd = MGA_CLEAR_CMD | DC_atype_blk;
+	else
+		cmd = MGA_CLEAR_CMD | DC_atype_rstr;
+
+	PRIM_OVERFLOW(dev, dev_priv, 35 * MGA_NR_SAREA_CLIPRECTS);
+
+	for (i = 0; i < nbox; i++) {
+		unsigned int height = pbox[i].y2 - pbox[i].y1;
+
+		if (flags & MGA_FRONT) {
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_PLNWT, clear_colormask);
+			PRIMOUTREG(MGAREG_YDSTLEN,
+				   (pbox[i].y1 << 16) | height);
+			PRIMOUTREG(MGAREG_FXBNDRY,
+				   (pbox[i].x2 << 16) | pbox[i].x1);
+
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_FCOL, clear_color);
+			PRIMOUTREG(MGAREG_DSTORG, dev_priv->frontOffset);
+			PRIMOUTREG(MGAREG_DWGCTL + MGAREG_MGA_EXEC, cmd);
+		}
+
+		if (flags & MGA_BACK) {
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_PLNWT, clear_colormask);
+			PRIMOUTREG(MGAREG_YDSTLEN,
+				   (pbox[i].y1 << 16) | height);
+			PRIMOUTREG(MGAREG_FXBNDRY,
+				   (pbox[i].x2 << 16) | pbox[i].x1);
+
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_FCOL, clear_color);
+			PRIMOUTREG(MGAREG_DSTORG, dev_priv->backOffset);
+			PRIMOUTREG(MGAREG_DWGCTL + MGAREG_MGA_EXEC, cmd);
+		}
+
+		if (flags & MGA_DEPTH) {
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_PLNWT, clear_depthmask);
+			PRIMOUTREG(MGAREG_YDSTLEN,
+				   (pbox[i].y1 << 16) | height);
+			PRIMOUTREG(MGAREG_FXBNDRY,
+				   (pbox[i].x2 << 16) | pbox[i].x1);
+
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_FCOL, clear_zval);
+			PRIMOUTREG(MGAREG_DSTORG, dev_priv->depthOffset);
+			PRIMOUTREG(MGAREG_DWGCTL + MGAREG_MGA_EXEC, cmd);
+		}
+	}
+
+	/* Force reset of DWGCTL */
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]);
+	PRIMADVANCE(dev_priv);
+}
+
+static void mga_dma_dispatch_swap(drm_device_t * dev)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int *regs = sarea_priv->ContextState;
+	int nbox = sarea_priv->nbox;
+	drm_clip_rect_t *pbox = sarea_priv->boxes;
+	int i;
+	int pixel_stride = dev_priv->stride / dev_priv->cpp;
+
+	PRIMLOCALS;
+
+	PRIM_OVERFLOW(dev, dev_priv, (MGA_NR_SAREA_CLIPRECTS * 5) + 20);
+
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DWGSYNC, 0x7100);
+	PRIMOUTREG(MGAREG_DWGSYNC, 0x7000);
+
+	PRIMOUTREG(MGAREG_DSTORG, dev_priv->frontOffset);
+	PRIMOUTREG(MGAREG_MACCESS, dev_priv->mAccess);
+	PRIMOUTREG(MGAREG_SRCORG, dev_priv->backOffset);
+	PRIMOUTREG(MGAREG_AR5, pixel_stride);
+
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DWGCTL, MGA_COPY_CMD);
+
+	for (i = 0; i < nbox; i++) {
+		unsigned int h = pbox[i].y2 - pbox[i].y1;
+		unsigned int start = pbox[i].y1 * pixel_stride;
+
+		PRIMOUTREG(MGAREG_AR0, start + pbox[i].x2 - 1);
+		PRIMOUTREG(MGAREG_AR3, start + pbox[i].x1);
+		PRIMOUTREG(MGAREG_FXBNDRY,
+			   pbox[i].x1 | ((pbox[i].x2 - 1) << 16));
+		PRIMOUTREG(MGAREG_YDSTLEN + MGAREG_MGA_EXEC,
+			   (pbox[i].y1 << 16) | h);
+	}
+
+	/* Force reset of DWGCTL */
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_SRCORG, 0);
+	PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]);
+
+	PRIMADVANCE(dev_priv);
+}
+
+int mga_clear_bufs(struct inode *inode, struct file *filp,
+		   unsigned int cmd, unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_mga_private_t *dev_priv =
+	    (drm_mga_private_t *) dev->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_mga_clear_t clear;
+
+	if (copy_from_user(&clear, (drm_mga_clear_t *) arg, sizeof(clear)))
+		return -EFAULT;
+
+	if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("mga_clear_bufs called without lock held\n");
+		return -EINVAL;
+	}
+
+	if (sarea_priv->nbox > MGA_NR_SAREA_CLIPRECTS)
+		sarea_priv->nbox = MGA_NR_SAREA_CLIPRECTS;
+
+	/* Make sure we restore the 3D state next time.
+	 */
+	dev_priv->sarea_priv->dirty |= MGA_UPLOAD_CTX;
+	mga_dma_dispatch_clear(dev, clear.flags,
+			       clear.clear_color,
+			       clear.clear_depth,
+			       clear.clear_color_mask,
+			       clear.clear_depth_mask);
+	PRIMUPDATE(dev_priv);
+	mga_flush_write_combine();
+	mga_dma_schedule(dev, 1);
+	return 0;
+}
+
+int mga_swap_bufs(struct inode *inode, struct file *filp,
+		  unsigned int cmd, unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_mga_private_t *dev_priv =
+	    (drm_mga_private_t *) dev->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+
+	if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("mga_swap_bufs called without lock held\n");
+		return -EINVAL;
+	}
+
+	if (sarea_priv->nbox > MGA_NR_SAREA_CLIPRECTS)
+		sarea_priv->nbox = MGA_NR_SAREA_CLIPRECTS;
+
+	/* Make sure we restore the 3D state next time.
+	 */
+	dev_priv->sarea_priv->dirty |= MGA_UPLOAD_CTX;
+	mga_dma_dispatch_swap(dev);
+	PRIMUPDATE(dev_priv);
+	set_bit(MGA_BUF_SWAP_PENDING,
+		&dev_priv->current_prim->buffer_status);
+	mga_flush_write_combine();
+	mga_dma_schedule(dev, 1);
+	return 0;
+}
+
+int mga_iload(struct inode *inode, struct file *filp,
+	      unsigned int cmd, unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_device_dma_t *dma = dev->dma;
+	drm_mga_private_t *dev_priv =
+	    (drm_mga_private_t *) dev->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_buf_t *buf;
+	drm_mga_buf_priv_t *buf_priv;
+	drm_mga_iload_t iload;
+	unsigned long bus_address;
+
+	if (copy_from_user(&iload, (drm_mga_iload_t *) arg, sizeof(iload)))
+		return -EFAULT;
+
+	if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("mga_iload called without lock held\n");
+		return -EINVAL;
+	}
+
+	if(iload.idx < 0 || iload.idx > dma->buf_count) return -EINVAL;
+	buf = dma->buflist[iload.idx];
+	buf_priv = buf->dev_private;
+	bus_address = buf->bus_address;
+
+	if (mgaVerifyIload(dev_priv,
+			   bus_address, iload.destOrg, iload.length)) {
+		mga_freelist_put(dev, buf);
+		return -EINVAL;
+	}
+
+	sarea_priv->dirty |= MGA_UPLOAD_CTX;
+
+	mga_dma_dispatch_tex_blit(dev, bus_address, iload.length,
+				  iload.destOrg);
+	AGEBUF(dev_priv, buf_priv);
+	buf_priv->discard = 1;
+	mga_freelist_put(dev, buf);
+	mga_flush_write_combine();
+	mga_dma_schedule(dev, 1);
+	return 0;
+}
+
+int mga_vertex(struct inode *inode, struct file *filp,
+	       unsigned int cmd, unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_mga_private_t *dev_priv =
+	    (drm_mga_private_t *) dev->dev_private;
+	drm_device_dma_t *dma = dev->dma;
+	drm_buf_t *buf;
+	drm_mga_buf_priv_t *buf_priv;
+	drm_mga_vertex_t vertex;
+
+	if (copy_from_user(&vertex, (drm_mga_vertex_t *) arg, sizeof(vertex)))
+		return -EFAULT;
+
+	if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("mga_vertex called without lock held\n");
+		return -EINVAL;
+	}
+
+	if(vertex.idx < 0 || vertex.idx > dma->buf_count) return -EINVAL;
+
+	buf = dma->buflist[vertex.idx];
+	buf_priv = buf->dev_private;
+
+	buf->used = vertex.used;
+	buf_priv->discard = vertex.discard;
+
+	if (!mgaVerifyState(dev_priv)) {
+		if (vertex.discard) {
+			if (buf_priv->dispatched == 1)
+				AGEBUF(dev_priv, buf_priv);
+			buf_priv->dispatched = 0;
+			mga_freelist_put(dev, buf);
+		}
+		return -EINVAL;
+	}
+
+	mga_dma_dispatch_vertex(dev, buf);
+
+	PRIMUPDATE(dev_priv);
+	mga_flush_write_combine();
+	mga_dma_schedule(dev, 1);
+	return 0;
+}
+
+
+int mga_indices(struct inode *inode, struct file *filp,
+		unsigned int cmd, unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_mga_private_t *dev_priv =
+	    (drm_mga_private_t *) dev->dev_private;
+	drm_device_dma_t *dma = dev->dma;
+	drm_buf_t *buf;
+	drm_mga_buf_priv_t *buf_priv;
+	drm_mga_indices_t indices;
+
+	if (copy_from_user(&indices,
+			   (drm_mga_indices_t *)arg, sizeof(indices)))
+		return -EFAULT;
+
+	if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("mga_indices called without lock held\n");
+		return -EINVAL;
+	}
+
+	if(indices.idx < 0 || indices.idx > dma->buf_count) return -EINVAL;
+	buf = dma->buflist[indices.idx];
+	buf_priv = buf->dev_private;
+
+	buf_priv->discard = indices.discard;
+
+	if (!mgaVerifyState(dev_priv)) {
+		if (indices.discard) {
+			if (buf_priv->dispatched == 1)
+				AGEBUF(dev_priv, buf_priv);
+			buf_priv->dispatched = 0;
+			mga_freelist_put(dev, buf);
+		}
+		return -EINVAL;
+	}
+
+	mga_dma_dispatch_indices(dev, buf, indices.start, indices.end);
+
+	PRIMUPDATE(dev_priv);
+	mga_flush_write_combine();
+	mga_dma_schedule(dev, 1);
+	return 0;
+}
+
+
+
+static int mga_dma_get_buffers(drm_device_t * dev, drm_dma_t * d)
+{
+	int i;
+	drm_buf_t *buf;
+
+	for (i = d->granted_count; i < d->request_count; i++) {
+		buf = mga_freelist_get(dev);
+		if (!buf)
+			break;
+		buf->pid = current->pid;
+		if (copy_to_user(&d->request_indices[i],
+				 &buf->idx, sizeof(buf->idx)))
+			return -EFAULT;
+		if (copy_to_user(&d->request_sizes[i],
+				 &buf->total, sizeof(buf->total)))
+			return -EFAULT;
+		++d->granted_count;
+	}
+	return 0;
+}
+
+int mga_dma(struct inode *inode, struct file *filp, unsigned int cmd,
+	    unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_device_dma_t *dma = dev->dma;
+	int retcode = 0;
+	drm_dma_t d;
+
+	if (copy_from_user(&d, (drm_dma_t *) arg, sizeof(d)))
+		return -EFAULT;
+
+	if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("mga_dma called without lock held\n");
+		return -EINVAL;
+	}
+
+	/* Please don't send us buffers.
+	 */
+	if (d.send_count != 0) {
+		DRM_ERROR
+		    ("Process %d trying to send %d buffers via drmDMA\n",
+		     current->pid, d.send_count);
+		return -EINVAL;
+	}
+
+	/* We'll send you buffers.
+	 */
+	if (d.request_count < 0 || d.request_count > dma->buf_count) {
+		DRM_ERROR
+		    ("Process %d trying to get %d buffers (of %d max)\n",
+		     current->pid, d.request_count, dma->buf_count);
+		return -EINVAL;
+	}
+
+	d.granted_count = 0;
+
+	if (d.request_count) {
+		retcode = mga_dma_get_buffers(dev, &d);
+	}
+
+	if (copy_to_user((drm_dma_t *) arg, &d, sizeof(d)))
+		return -EFAULT;
+	return retcode;
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/proc.c linux/drivers/char/drm-4.0/proc.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/proc.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/proc.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,578 @@
+/* proc.c -- /proc support for DRM -*- linux-c -*-
+ * Created: Mon Jan 11 09:48:47 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+static struct proc_dir_entry *drm_root	   = NULL;
+static struct proc_dir_entry *drm_dev_root = NULL;
+static char		     drm_slot_name[64];
+
+static int	   drm_name_info(char *buf, char **start, off_t offset,
+				 int len, int *eof, void *data);
+static int	   drm_vm_info(char *buf, char **start, off_t offset,
+			       int len, int *eof, void *data);
+static int	   drm_clients_info(char *buf, char **start, off_t offset,
+				    int len, int *eof, void *data);
+static int	   drm_queues_info(char *buf, char **start, off_t offset,
+				   int len, int *eof, void *data);
+static int	   drm_bufs_info(char *buf, char **start, off_t offset,
+				 int len, int *eof, void *data);
+#if DRM_DEBUG_CODE
+static int	   drm_vma_info(char *buf, char **start, off_t offset,
+				int len, int *eof, void *data);
+#endif
+#if DRM_DMA_HISTOGRAM
+static int	   drm_histo_info(char *buf, char **start, off_t offset,
+				  int len, int *eof, void *data);
+#endif
+
+struct drm_proc_list {
+	const char *name;
+	int	   (*f)(char *, char **, off_t, int, int *, void *);
+} drm_proc_list[] = {
+	{ "name",    drm_name_info    },
+	{ "mem",     drm_mem_info     },
+	{ "vm",	     drm_vm_info      },
+	{ "clients", drm_clients_info },
+	{ "queues",  drm_queues_info  },
+	{ "bufs",    drm_bufs_info    },
+#if DRM_DEBUG_CODE
+	{ "vma",     drm_vma_info     },
+#endif
+#if DRM_DMA_HISTOGRAM
+	{ "histo",   drm_histo_info   },
+#endif
+};
+#define DRM_PROC_ENTRIES (sizeof(drm_proc_list)/sizeof(drm_proc_list[0]))
+
+int drm_proc_init(drm_device_t *dev)
+{
+	struct proc_dir_entry *ent;
+	int		      i, j;
+
+	drm_root = create_proc_entry("dri", S_IFDIR, NULL);
+	if (!drm_root) {
+		DRM_ERROR("Cannot create /proc/dri\n");
+		return -1;
+	}
+
+				/* Instead of doing this search, we should
+				   add some global support for /proc/dri. */
+	for (i = 0; i < 8; i++) {
+		sprintf(drm_slot_name, "dri/%d", i);
+		drm_dev_root = create_proc_entry(drm_slot_name, S_IFDIR, NULL);
+		if (!drm_dev_root) {
+			DRM_ERROR("Cannot create /proc/%s\n", drm_slot_name);
+			remove_proc_entry("dri", NULL);
+			break;
+		}
+		if (drm_dev_root->nlink == 2) break;
+		drm_dev_root = NULL;
+	}
+	if (!drm_dev_root) {
+		DRM_ERROR("Cannot find slot in /proc/dri\n");
+		return -1;
+	}
+
+	for (i = 0; i < DRM_PROC_ENTRIES; i++) {
+		ent = create_proc_entry(drm_proc_list[i].name,
+					S_IFREG|S_IRUGO, drm_dev_root);
+		if (!ent) {
+			DRM_ERROR("Cannot create /proc/%s/%s\n",
+				  drm_slot_name, drm_proc_list[i].name);
+			for (j = 0; j < i; j++)
+				remove_proc_entry(drm_proc_list[i].name,
+						  drm_dev_root);
+			remove_proc_entry(drm_slot_name, NULL);
+			remove_proc_entry("dri", NULL);
+			return -1;
+		}
+		ent->read_proc = drm_proc_list[i].f;
+		ent->data      = dev;
+	}
+
+	return 0;
+}
+
+
+int drm_proc_cleanup(void)
+{
+	int i;
+	
+	if (drm_root) {
+		if (drm_dev_root) {
+			for (i = 0; i < DRM_PROC_ENTRIES; i++) {
+				remove_proc_entry(drm_proc_list[i].name,
+						  drm_dev_root);
+			}
+			remove_proc_entry(drm_slot_name, NULL);
+		}
+		remove_proc_entry("dri", NULL);
+		remove_proc_entry(DRM_NAME, NULL);
+	}
+	drm_root = drm_dev_root = NULL;
+	return 0;
+}
+
+static int drm_name_info(char *buf, char **start, off_t offset, int len,
+			 int *eof, void *data)
+{
+	drm_device_t *dev = (drm_device_t *)data;
+
+	if (offset > 0) return 0; /* no partial requests */
+	len  = 0;
+	*eof = 1;
+
+	if (dev->unique) {
+		DRM_PROC_PRINT("%s 0x%x %s\n",
+			       dev->name, dev->device, dev->unique);
+	} else {
+		DRM_PROC_PRINT("%s 0x%x\n", dev->name, dev->device);
+	}
+	return len;
+}
+
+static int _drm_vm_info(char *buf, char **start, off_t offset, int len,
+			int *eof, void *data)
+{
+	drm_device_t *dev = (drm_device_t *)data;
+	drm_map_t    *map;
+				/* Hardcoded from _DRM_FRAME_BUFFER,
+                                   _DRM_REGISTERS, _DRM_SHM, and
+                                   _DRM_AGP. */
+	const char   *types[] = { "FB", "REG", "SHM", "AGP" };
+	const char   *type;
+	int	     i;
+
+	if (offset > 0) return 0; /* no partial requests */
+	len  = 0;
+	*eof = 1;
+	DRM_PROC_PRINT("slot	 offset	      size type flags	 "
+		       "address mtrr\n\n");
+	for (i = 0; i < dev->map_count; i++) {
+		map = dev->maplist[i];
+		if (map->type < 0 || map->type > 3) type = "??";
+		else				    type = types[map->type];
+		DRM_PROC_PRINT("%4d 0x%08lx 0x%08lx %4.4s  0x%02x 0x%08lx ",
+			       i,
+			       map->offset,
+			       map->size,
+			       type,
+			       map->flags,
+			       (unsigned long)map->handle);
+		if (map->mtrr < 0) {
+			DRM_PROC_PRINT("none\n");
+		} else {
+			DRM_PROC_PRINT("%4d\n", map->mtrr);
+		}
+	}
+
+	return len;
+}
+
+static int drm_vm_info(char *buf, char **start, off_t offset, int len,
+		       int *eof, void *data)
+{
+	drm_device_t *dev = (drm_device_t *)data;
+	int	     ret;
+
+	down(&dev->struct_sem);
+	ret = _drm_vm_info(buf, start, offset, len, eof, data);
+	up(&dev->struct_sem);
+	return ret;
+}
+
+
+static int _drm_queues_info(char *buf, char **start, off_t offset, int len,
+			    int *eof, void *data)
+{
+	drm_device_t *dev = (drm_device_t *)data;
+	int	     i;
+	drm_queue_t  *q;
+
+	if (offset > 0) return 0; /* no partial requests */
+	len  = 0;
+	*eof = 1;
+	DRM_PROC_PRINT("  ctx/flags   use   fin"
+		       "   blk/rw/rwf  wait    flushed	   queued"
+		       "      locks\n\n");
+	for (i = 0; i < dev->queue_count; i++) {
+		q = dev->queuelist[i];
+		atomic_inc(&q->use_count);
+		DRM_PROC_PRINT_RET(atomic_dec(&q->use_count),
+				   "%5d/0x%03x %5d %5d"
+				   " %5d/%c%c/%c%c%c %5Zd %10d %10d %10d\n",
+				   i,
+				   q->flags,
+				   atomic_read(&q->use_count),
+				   atomic_read(&q->finalization),
+				   atomic_read(&q->block_count),
+				   atomic_read(&q->block_read) ? 'r' : '-',
+				   atomic_read(&q->block_write) ? 'w' : '-',
+				   waitqueue_active(&q->read_queue) ? 'r':'-',
+				   waitqueue_active(&q->write_queue) ? 'w':'-',
+				   waitqueue_active(&q->flush_queue) ? 'f':'-',
+				   DRM_BUFCOUNT(&q->waitlist),
+				   atomic_read(&q->total_flushed),
+				   atomic_read(&q->total_queued),
+				   atomic_read(&q->total_locks));
+		atomic_dec(&q->use_count);
+	}
+	
+	return len;
+}
+
+static int drm_queues_info(char *buf, char **start, off_t offset, int len,
+			   int *eof, void *data)
+{
+	drm_device_t *dev = (drm_device_t *)data;
+	int	     ret;
+
+	down(&dev->struct_sem);
+	ret = _drm_queues_info(buf, start, offset, len, eof, data);
+	up(&dev->struct_sem);
+	return ret;
+}
+
+/* drm_bufs_info is called whenever a process reads
+   /dev/drm/<dev>/bufs. */
+
+static int _drm_bufs_info(char *buf, char **start, off_t offset, int len,
+			  int *eof, void *data)
+{
+	drm_device_t	 *dev = (drm_device_t *)data;
+	drm_device_dma_t *dma = dev->dma;
+	int		 i;
+
+	if (!dma)	return 0;
+	if (offset > 0) return 0; /* no partial requests */
+	len  = 0;
+	*eof = 1;
+	DRM_PROC_PRINT(" o     size count  free	 segs pages    kB\n\n");
+	for (i = 0; i <= DRM_MAX_ORDER; i++) {
+		if (dma->bufs[i].buf_count)
+			DRM_PROC_PRINT("%2d %8d %5d %5d %5d %5d %5ld\n",
+				       i,
+				       dma->bufs[i].buf_size,
+				       dma->bufs[i].buf_count,
+				       atomic_read(&dma->bufs[i]
+						   .freelist.count),
+				       dma->bufs[i].seg_count,
+				       dma->bufs[i].seg_count
+				       *(1 << dma->bufs[i].page_order),
+				       (dma->bufs[i].seg_count
+					* (1 << dma->bufs[i].page_order))
+				       * PAGE_SIZE / 1024);
+	}
+	DRM_PROC_PRINT("\n");
+	for (i = 0; i < dma->buf_count; i++) {
+		if (i && !(i%32)) DRM_PROC_PRINT("\n");
+		DRM_PROC_PRINT(" %d", dma->buflist[i]->list);
+	}
+	DRM_PROC_PRINT("\n");
+
+	return len;
+}
+
+static int drm_bufs_info(char *buf, char **start, off_t offset, int len,
+			 int *eof, void *data)
+{
+	drm_device_t *dev = (drm_device_t *)data;
+	int	     ret;
+
+	down(&dev->struct_sem);
+	ret = _drm_bufs_info(buf, start, offset, len, eof, data);
+	up(&dev->struct_sem);
+	return ret;
+}
+
+
+static int _drm_clients_info(char *buf, char **start, off_t offset, int len,
+			     int *eof, void *data)
+{
+	drm_device_t *dev = (drm_device_t *)data;
+	drm_file_t   *priv;
+
+	if (offset > 0) return 0; /* no partial requests */
+	len  = 0;
+	*eof = 1;
+	DRM_PROC_PRINT("a dev	pid    uid	magic	  ioctls\n\n");
+	for (priv = dev->file_first; priv; priv = priv->next) {
+		DRM_PROC_PRINT("%c %3d %5d %5d %10u %10lu\n",
+			       priv->authenticated ? 'y' : 'n',
+			       priv->minor,
+			       priv->pid,
+			       priv->uid,
+			       priv->magic,
+			       priv->ioctl_count);
+	}
+
+	return len;
+}
+
+static int drm_clients_info(char *buf, char **start, off_t offset, int len,
+			    int *eof, void *data)
+{
+	drm_device_t *dev = (drm_device_t *)data;
+	int	     ret;
+
+	down(&dev->struct_sem);
+	ret = _drm_clients_info(buf, start, offset, len, eof, data);
+	up(&dev->struct_sem);
+	return ret;
+}
+
+#if DRM_DEBUG_CODE
+
+#define DRM_VMA_VERBOSE 0
+
+static int _drm_vma_info(char *buf, char **start, off_t offset, int len,
+			 int *eof, void *data)
+{
+	drm_device_t	      *dev = (drm_device_t *)data;
+	drm_vma_entry_t	      *pt;
+	struct vm_area_struct *vma;
+#if DRM_VMA_VERBOSE
+	unsigned long	      i;
+	unsigned long	      address;
+	pgd_t		      *pgd;
+	pmd_t		      *pmd;
+	pte_t		      *pte;
+#endif
+#if defined(__i386__)
+	unsigned int	      pgprot;
+#endif
+
+	if (offset > 0) return 0; /* no partial requests */
+	len  = 0;
+	*eof = 1;
+	DRM_PROC_PRINT("vma use count: %d, high_memory = %p, 0x%08lx\n",
+		       atomic_read(&dev->vma_count),
+		       high_memory, virt_to_phys(high_memory));
+	for (pt = dev->vmalist; pt; pt = pt->next) {
+		if (!(vma = pt->vma)) continue;
+		DRM_PROC_PRINT("\n%5d 0x%08lx-0x%08lx %c%c%c%c%c%c 0x%08lx",
+			       pt->pid,
+			       vma->vm_start,
+			       vma->vm_end,
+			       vma->vm_flags & VM_READ	   ? 'r' : '-',
+			       vma->vm_flags & VM_WRITE	   ? 'w' : '-',
+			       vma->vm_flags & VM_EXEC	   ? 'x' : '-',
+			       vma->vm_flags & VM_MAYSHARE ? 's' : 'p',
+			       vma->vm_flags & VM_LOCKED   ? 'l' : '-',
+			       vma->vm_flags & VM_IO	   ? 'i' : '-',
+			       VM_OFFSET(vma));
+		
+#if defined(__i386__)
+		pgprot = pgprot_val(vma->vm_page_prot);
+		DRM_PROC_PRINT(" %c%c%c%c%c%c%c%c%c",
+			       pgprot & _PAGE_PRESENT  ? 'p' : '-',
+			       pgprot & _PAGE_RW       ? 'w' : 'r',
+			       pgprot & _PAGE_USER     ? 'u' : 's',
+			       pgprot & _PAGE_PWT      ? 't' : 'b',
+			       pgprot & _PAGE_PCD      ? 'u' : 'c',
+			       pgprot & _PAGE_ACCESSED ? 'a' : '-',
+			       pgprot & _PAGE_DIRTY    ? 'd' : '-',
+			       pgprot & _PAGE_PSE      ? 'm' : 'k',
+			       pgprot & _PAGE_GLOBAL   ? 'g' : 'l' );
+#endif		
+		DRM_PROC_PRINT("\n");
+#if 0
+		for (i = vma->vm_start; i < vma->vm_end; i += PAGE_SIZE) {
+			pgd = pgd_offset(vma->vm_mm, i);
+			pmd = pmd_offset(pgd, i);
+			pte = pte_offset(pmd, i);
+			if (pte_present(*pte)) {
+				address = __pa(pte_page(*pte))
+					+ (i & (PAGE_SIZE-1));
+				DRM_PROC_PRINT("      0x%08lx -> 0x%08lx"
+					       " %c%c%c%c%c\n",
+					       i,
+					       address,
+					       pte_read(*pte)  ? 'r' : '-',
+					       pte_write(*pte) ? 'w' : '-',
+					       pte_exec(*pte)  ? 'x' : '-',
+					       pte_dirty(*pte) ? 'd' : '-',
+					       pte_young(*pte) ? 'a' : '-' );
+			} else {
+				DRM_PROC_PRINT("      0x%08lx\n", i);
+			}
+		}
+#endif
+	}
+	
+	return len;
+}
+
+static int drm_vma_info(char *buf, char **start, off_t offset, int len,
+			int *eof, void *data)
+{
+	drm_device_t *dev = (drm_device_t *)data;
+	int	     ret;
+
+	down(&dev->struct_sem);
+	ret = _drm_vma_info(buf, start, offset, len, eof, data);
+	up(&dev->struct_sem);
+	return ret;
+}
+#endif
+
+
+#if DRM_DMA_HISTOGRAM
+static int _drm_histo_info(char *buf, char **start, off_t offset, int len,
+			   int *eof, void *data)
+{
+	drm_device_t	 *dev = (drm_device_t *)data;
+	drm_device_dma_t *dma = dev->dma;
+	int		 i;
+	unsigned long	 slot_value = DRM_DMA_HISTOGRAM_INITIAL;
+	unsigned long	 prev_value = 0;
+	drm_buf_t	 *buffer;
+
+	if (offset > 0) return 0; /* no partial requests */
+	len  = 0;
+	*eof = 1;
+
+	DRM_PROC_PRINT("general statistics:\n");
+	DRM_PROC_PRINT("total	 %10u\n", atomic_read(&dev->histo.total));
+	DRM_PROC_PRINT("open	 %10u\n", atomic_read(&dev->total_open));
+	DRM_PROC_PRINT("close	 %10u\n", atomic_read(&dev->total_close));
+	DRM_PROC_PRINT("ioctl	 %10u\n", atomic_read(&dev->total_ioctl));
+	DRM_PROC_PRINT("irq	 %10u\n", atomic_read(&dev->total_irq));
+	DRM_PROC_PRINT("ctx	 %10u\n", atomic_read(&dev->total_ctx));
+	
+	DRM_PROC_PRINT("\nlock statistics:\n");
+	DRM_PROC_PRINT("locks	 %10u\n", atomic_read(&dev->total_locks));
+	DRM_PROC_PRINT("unlocks	 %10u\n", atomic_read(&dev->total_unlocks));
+	DRM_PROC_PRINT("contends %10u\n", atomic_read(&dev->total_contends));
+	DRM_PROC_PRINT("sleeps	 %10u\n", atomic_read(&dev->total_sleeps));
+
+
+	if (dma) {
+		DRM_PROC_PRINT("\ndma statistics:\n");
+		DRM_PROC_PRINT("prio	 %10u\n",
+			       atomic_read(&dma->total_prio));
+		DRM_PROC_PRINT("bytes	 %10u\n",
+			       atomic_read(&dma->total_bytes));
+		DRM_PROC_PRINT("dmas	 %10u\n",
+			       atomic_read(&dma->total_dmas));
+		DRM_PROC_PRINT("missed:\n");
+		DRM_PROC_PRINT("  dma	 %10u\n",
+			       atomic_read(&dma->total_missed_dma));
+		DRM_PROC_PRINT("  lock	 %10u\n",
+			       atomic_read(&dma->total_missed_lock));
+		DRM_PROC_PRINT("  free	 %10u\n",
+			       atomic_read(&dma->total_missed_free));
+		DRM_PROC_PRINT("  sched	 %10u\n",
+			       atomic_read(&dma->total_missed_sched));
+		DRM_PROC_PRINT("tried	 %10u\n",
+			       atomic_read(&dma->total_tried));
+		DRM_PROC_PRINT("hit	 %10u\n",
+			       atomic_read(&dma->total_hit));
+		DRM_PROC_PRINT("lost	 %10u\n",
+			       atomic_read(&dma->total_lost));
+		
+		buffer = dma->next_buffer;
+		if (buffer) {
+			DRM_PROC_PRINT("next_buffer %7d\n", buffer->idx);
+		} else {
+			DRM_PROC_PRINT("next_buffer    none\n");
+		}
+		buffer = dma->this_buffer;
+		if (buffer) {
+			DRM_PROC_PRINT("this_buffer %7d\n", buffer->idx);
+		} else {
+			DRM_PROC_PRINT("this_buffer    none\n");
+		}
+	}
+	
+
+	DRM_PROC_PRINT("\nvalues:\n");
+	if (dev->lock.hw_lock) {
+		DRM_PROC_PRINT("lock	       0x%08x\n",
+			       dev->lock.hw_lock->lock);
+	} else {
+		DRM_PROC_PRINT("lock		     none\n");
+	}
+	DRM_PROC_PRINT("context_flag   0x%08lx\n", dev->context_flag);
+	DRM_PROC_PRINT("interrupt_flag 0x%08lx\n", dev->interrupt_flag);
+	DRM_PROC_PRINT("dma_flag       0x%08lx\n", dev->dma_flag);
+
+	DRM_PROC_PRINT("queue_count    %10d\n",	 dev->queue_count);
+	DRM_PROC_PRINT("last_context   %10d\n",	 dev->last_context);
+	DRM_PROC_PRINT("last_switch    %10lu\n", dev->last_switch);
+	DRM_PROC_PRINT("last_checked   %10d\n",	 dev->last_checked);
+		
+	
+	DRM_PROC_PRINT("\n		       q2d	  d2c	     c2f"
+		       "	q2c	   q2f	      dma	 sch"
+		       "	ctx	  lacq	     lhld\n\n");
+	for (i = 0; i < DRM_DMA_HISTOGRAM_SLOTS; i++) {
+		DRM_PROC_PRINT("%s %10lu %10u %10u %10u %10u %10u"
+			       " %10u %10u %10u %10u %10u\n",
+			       i == DRM_DMA_HISTOGRAM_SLOTS - 1 ? ">=" : "< ",
+			       i == DRM_DMA_HISTOGRAM_SLOTS - 1
+			       ? prev_value : slot_value ,
+			       
+			       atomic_read(&dev->histo
+					   .queued_to_dispatched[i]),
+			       atomic_read(&dev->histo
+					   .dispatched_to_completed[i]),
+			       atomic_read(&dev->histo
+					   .completed_to_freed[i]),
+			       
+			       atomic_read(&dev->histo
+					   .queued_to_completed[i]),
+			       atomic_read(&dev->histo
+					   .queued_to_freed[i]),
+			       atomic_read(&dev->histo.dma[i]),
+			       atomic_read(&dev->histo.schedule[i]),
+			       atomic_read(&dev->histo.ctx[i]),
+			       atomic_read(&dev->histo.lacq[i]),
+			       atomic_read(&dev->histo.lhld[i]));
+		prev_value = slot_value;
+		slot_value = DRM_DMA_HISTOGRAM_NEXT(slot_value);
+	}
+	return len;
+}
+
+static int drm_histo_info(char *buf, char **start, off_t offset, int len,
+			  int *eof, void *data)
+{
+	drm_device_t *dev = (drm_device_t *)data;
+	int	     ret;
+
+	down(&dev->struct_sem);
+	ret = _drm_histo_info(buf, start, offset, len, eof, data);
+	up(&dev->struct_sem);
+	return ret;
+}
+#endif
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/r128_bufs.c linux/drivers/char/drm-4.0/r128_bufs.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/r128_bufs.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/r128_bufs.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,319 @@
+/* r128_bufs.c -- IOCTLs to manage buffers -*- linux-c -*-
+ * Created: Wed Apr 12 16:19:08 2000 by kevin@precisioninsight.com
+ *
+ * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Kevin E. Martin <martin@valinux.com>
+ *          Rickard E. (Rik) Faith <faith@valinux.com>
+ *	    Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include "drmP.h"
+#include "r128_drv.h"
+#include "linux/un.h"
+
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+int r128_addbufs_agp(struct inode *inode, struct file *filp, unsigned int cmd,
+		     unsigned long arg)
+{
+	drm_file_t       *priv = filp->private_data;
+	drm_device_t     *dev  = priv->dev;
+	drm_device_dma_t *dma  = dev->dma;
+	drm_buf_desc_t    request;
+	drm_buf_entry_t  *entry;
+	drm_buf_t        *buf;
+	unsigned long     offset;
+	unsigned long     agp_offset;
+	int               count;
+	int               order;
+	int               size;
+	int               alignment;
+	int               page_order;
+	int               total;
+	int               byte_count;
+	int               i;
+
+	if (!dma) return -EINVAL;
+
+	if (copy_from_user(&request,
+			   (drm_buf_desc_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	count      = request.count;
+	order      = drm_order(request.size);
+	size       = 1 << order;
+
+	alignment  = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size):size;
+	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
+	total      = PAGE_SIZE << page_order;
+
+	byte_count = 0;
+	agp_offset = dev->agp->base + request.agp_start;
+
+	DRM_DEBUG("count:      %d\n",  count);
+	DRM_DEBUG("order:      %d\n",  order);
+	DRM_DEBUG("size:       %d\n",  size);
+	DRM_DEBUG("agp_offset: %ld\n", agp_offset);
+	DRM_DEBUG("alignment:  %d\n",  alignment);
+	DRM_DEBUG("page_order: %d\n",  page_order);
+	DRM_DEBUG("total:      %d\n",  total);
+
+	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+	if (dev->queue_count) return -EBUSY; /* Not while in use */
+
+	spin_lock(&dev->count_lock);
+	if (dev->buf_use) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	atomic_inc(&dev->buf_alloc);
+	spin_unlock(&dev->count_lock);
+
+	down(&dev->struct_sem);
+	entry = &dma->bufs[order];
+	if (entry->buf_count) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM; /* May only call once for each order */
+	}
+
+	/* Might be a poor limit, but take that up with XFree86 
+	   if its a problem */
+	   
+	if(count < 0 || count > 4096)
+	{
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -EINVAL;
+	}
+		
+	entry->buflist = drm_alloc(count * sizeof(*entry->buflist),
+				   DRM_MEM_BUFS);
+	if (!entry->buflist) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM;
+	}
+	memset(entry->buflist, 0, count * sizeof(*entry->buflist));
+
+	entry->buf_size   = size;
+	entry->page_order = page_order;
+	offset            = 0;
+
+	for (offset = 0;
+	     entry->buf_count < count;
+	     offset += alignment, ++entry->buf_count) {
+		buf          = &entry->buflist[entry->buf_count];
+		buf->idx     = dma->buf_count + entry->buf_count;
+		buf->total   = alignment;
+		buf->order   = order;
+		buf->used    = 0;
+		buf->offset  = (dma->byte_count + offset);
+		buf->address = (void *)(agp_offset + offset);
+		buf->next    = NULL;
+		buf->waiting = 0;
+		buf->pending = 0;
+		init_waitqueue_head(&buf->dma_wait);
+		buf->pid     = 0;
+
+		buf->dev_priv_size = sizeof(drm_r128_buf_priv_t);
+		buf->dev_private   = drm_alloc(sizeof(drm_r128_buf_priv_t),
+					       DRM_MEM_BUFS);
+		memset(buf->dev_private, 0, buf->dev_priv_size);
+
+#if DRM_DMA_HISTOGRAM
+		buf->time_queued     = 0;
+		buf->time_dispatched = 0;
+		buf->time_completed  = 0;
+		buf->time_freed      = 0;
+#endif
+
+		byte_count += PAGE_SIZE << page_order;
+
+		DRM_DEBUG("buffer %d @ %p\n",
+			  entry->buf_count, buf->address);
+	}
+
+	DRM_DEBUG("byte_count: %d\n", byte_count);
+
+	dma->buflist = drm_realloc(dma->buflist,
+				   dma->buf_count * sizeof(*dma->buflist),
+				   (dma->buf_count + entry->buf_count)
+				   * sizeof(*dma->buflist),
+				   DRM_MEM_BUFS);
+	for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++)
+		dma->buflist[i] = &entry->buflist[i - dma->buf_count];
+
+	dma->buf_count  += entry->buf_count;
+	dma->byte_count += byte_count;
+
+	drm_freelist_create(&entry->freelist, entry->buf_count);
+	for (i = 0; i < entry->buf_count; i++) {
+		drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]);
+	}
+
+	up(&dev->struct_sem);
+
+	request.count = entry->buf_count;
+	request.size  = size;
+
+	if (copy_to_user((drm_buf_desc_t *)arg,
+			 &request,
+			 sizeof(request)))
+		return -EFAULT;
+
+	dma->flags = _DRM_DMA_USE_AGP;
+
+	atomic_dec(&dev->buf_alloc);
+	return 0;
+}
+#endif
+
+int r128_addbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t		*priv		= filp->private_data;
+	drm_device_t		*dev		= priv->dev;
+	drm_r128_private_t	*dev_priv	= dev->dev_private;
+	drm_buf_desc_t		request;
+
+	if (!dev_priv || dev_priv->is_pci) return -EINVAL;
+
+	if (copy_from_user(&request,
+			   (drm_buf_desc_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	if (request.flags & _DRM_AGP_BUFFER)
+		return r128_addbufs_agp(inode, filp, cmd, arg);
+	else
+#endif
+		return -EINVAL;
+}
+
+int r128_mapbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t		*priv		= filp->private_data;
+	drm_device_t		*dev		= priv->dev;
+	drm_r128_private_t	*dev_priv	= dev->dev_private;
+	drm_device_dma_t	*dma		= dev->dma;
+	int			 retcode	= 0;
+	const int		 zero		= 0;
+	unsigned long		 virtual;
+	unsigned long		 address;
+	drm_buf_map_t		 request;
+	int			 i;
+
+	if (!dma || !dev_priv || dev_priv->is_pci) return -EINVAL;
+
+	DRM_DEBUG("\n");
+
+	spin_lock(&dev->count_lock);
+	if (atomic_read(&dev->buf_alloc)) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	++dev->buf_use;		/* Can't allocate more after this call */
+	spin_unlock(&dev->count_lock);
+
+	if (copy_from_user(&request,
+			   (drm_buf_map_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	if (request.count >= dma->buf_count) {
+		if (dma->flags & _DRM_DMA_USE_AGP) {
+			drm_map_t *map;
+
+			map = dev_priv->buffers;
+			if (!map) {
+				retcode = -EINVAL;
+				goto done;
+			}
+
+			down_write(&current->mm->mmap_sem);
+			virtual = do_mmap(filp, 0, map->size,
+					  PROT_READ|PROT_WRITE,
+					  MAP_SHARED,
+					  (unsigned long)map->offset);
+			up_write(&current->mm->mmap_sem);
+		} else {
+			down_write(&current->mm->mmap_sem);
+			virtual = do_mmap(filp, 0, dma->byte_count,
+					  PROT_READ|PROT_WRITE, MAP_SHARED, 0);
+			up_write(&current->mm->mmap_sem);
+		}
+		if (virtual > -1024UL) {
+			/* Real error */
+			retcode = (signed long)virtual;
+			goto done;
+		}
+		request.virtual = (void *)virtual;
+
+		for (i = 0; i < dma->buf_count; i++) {
+			if (copy_to_user(&request.list[i].idx,
+					 &dma->buflist[i]->idx,
+					 sizeof(request.list[0].idx))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			if (copy_to_user(&request.list[i].total,
+					 &dma->buflist[i]->total,
+					 sizeof(request.list[0].total))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			if (copy_to_user(&request.list[i].used,
+					 &zero,
+					 sizeof(zero))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			address = virtual + dma->buflist[i]->offset;
+			if (copy_to_user(&request.list[i].address,
+					 &address,
+					 sizeof(address))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+		}
+	}
+ done:
+	request.count = dma->buf_count;
+	DRM_DEBUG("%d buffers, retcode = %d\n", request.count, retcode);
+
+	if (copy_to_user((drm_buf_map_t *)arg,
+			 &request,
+			 sizeof(request)))
+		return -EFAULT;
+
+	return retcode;
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/r128_cce.c linux/drivers/char/drm-4.0/r128_cce.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/r128_cce.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/r128_cce.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,1253 @@
+/* r128_cce.c -- ATI Rage 128 driver -*- linux-c -*-
+ * Created: Wed Apr  5 19:24:19 2000 by kevin@precisioninsight.com
+ *
+ * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Gareth Hughes <gareth@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "r128_drv.h"
+
+#include <linux/interrupt.h>	/* For task queue support */
+#include <linux/delay.h>
+
+
+/* FIXME: Temporary CCE packet buffer */
+u32 r128_cce_buffer[(1 << 14)] __attribute__ ((aligned (32)));
+
+/* CCE microcode (from ATI) */
+static u32 r128_cce_microcode[] = {
+	0, 276838400, 0, 268449792, 2, 142, 2, 145, 0, 1076765731, 0,
+	1617039951, 0, 774592877, 0, 1987540286, 0, 2307490946U, 0,
+	599558925, 0, 589505315, 0, 596487092, 0, 589505315, 1,
+	11544576, 1, 206848, 1, 311296, 1, 198656, 2, 912273422, 11,
+	262144, 0, 0, 1, 33559837, 1, 7438, 1, 14809, 1, 6615, 12, 28,
+	1, 6614, 12, 28, 2, 23, 11, 18874368, 0, 16790922, 1, 409600, 9,
+	30, 1, 147854772, 16, 420483072, 3, 8192, 0, 10240, 1, 198656,
+	1, 15630, 1, 51200, 10, 34858, 9, 42, 1, 33559823, 2, 10276, 1,
+	15717, 1, 15718, 2, 43, 1, 15936948, 1, 570480831, 1, 14715071,
+	12, 322123831, 1, 33953125, 12, 55, 1, 33559908, 1, 15718, 2,
+	46, 4, 2099258, 1, 526336, 1, 442623, 4, 4194365, 1, 509952, 1,
+	459007, 3, 0, 12, 92, 2, 46, 12, 176, 1, 15734, 1, 206848, 1,
+	18432, 1, 133120, 1, 100670734, 1, 149504, 1, 165888, 1,
+	15975928, 1, 1048576, 6, 3145806, 1, 15715, 16, 2150645232U, 2,
+	268449859, 2, 10307, 12, 176, 1, 15734, 1, 15735, 1, 15630, 1,
+	15631, 1, 5253120, 6, 3145810, 16, 2150645232U, 1, 15864, 2, 82,
+	1, 343310, 1, 1064207, 2, 3145813, 1, 15728, 1, 7817, 1, 15729,
+	3, 15730, 12, 92, 2, 98, 1, 16168, 1, 16167, 1, 16002, 1, 16008,
+	1, 15974, 1, 15975, 1, 15990, 1, 15976, 1, 15977, 1, 15980, 0,
+	15981, 1, 10240, 1, 5253120, 1, 15720, 1, 198656, 6, 110, 1,
+	180224, 1, 103824738, 2, 112, 2, 3145839, 0, 536885440, 1,
+	114880, 14, 125, 12, 206975, 1, 33559995, 12, 198784, 0,
+	33570236, 1, 15803, 0, 15804, 3, 294912, 1, 294912, 3, 442370,
+	1, 11544576, 0, 811612160, 1, 12593152, 1, 11536384, 1,
+	14024704, 7, 310382726, 0, 10240, 1, 14796, 1, 14797, 1, 14793,
+	1, 14794, 0, 14795, 1, 268679168, 1, 9437184, 1, 268449792, 1,
+	198656, 1, 9452827, 1, 1075854602, 1, 1075854603, 1, 557056, 1,
+	114880, 14, 159, 12, 198784, 1, 1109409213, 12, 198783, 1,
+	1107312059, 12, 198784, 1, 1109409212, 2, 162, 1, 1075854781, 1,
+	1073757627, 1, 1075854780, 1, 540672, 1, 10485760, 6, 3145894,
+	16, 274741248, 9, 168, 3, 4194304, 3, 4209949, 0, 0, 0, 256, 14,
+	174, 1, 114857, 1, 33560007, 12, 176, 0, 10240, 1, 114858, 1,
+	33560018, 1, 114857, 3, 33560007, 1, 16008, 1, 114874, 1,
+	33560360, 1, 114875, 1, 33560154, 0, 15963, 0, 256, 0, 4096, 1,
+	409611, 9, 188, 0, 10240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+
+#define DO_REMAP(_m) (_m)->handle = drm_ioremap((_m)->offset, (_m)->size)
+
+#define DO_REMAPFREE(_m)                                                    \
+	do {                                                                \
+		if ((_m)->handle && (_m)->size)                             \
+			drm_ioremapfree((_m)->handle, (_m)->size);          \
+	} while (0)
+
+#define DO_FIND_MAP(_m, _o)                                                 \
+	do {                                                                \
+		int _i;                                                     \
+		for (_i = 0; _i < dev->map_count; _i++) {                   \
+			if (dev->maplist[_i]->offset == _o) {               \
+				_m = dev->maplist[_i];                      \
+				break;                                      \
+			}                                                   \
+		}                                                           \
+	} while (0)
+
+
+int R128_READ_PLL(drm_device_t *dev, int addr)
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+
+	R128_WRITE8(R128_CLOCK_CNTL_INDEX, addr & 0x1f);
+	return R128_READ(R128_CLOCK_CNTL_DATA);
+}
+
+#if 0
+static void r128_status( drm_r128_private_t *dev_priv )
+{
+	printk( "GUI_STAT           = 0x%08x\n",
+		(unsigned int)R128_READ( R128_GUI_STAT ) );
+	printk( "PM4_STAT           = 0x%08x\n",
+		(unsigned int)R128_READ( R128_PM4_STAT ) );
+	printk( "PM4_BUFFER_DL_WPTR = 0x%08x\n",
+		(unsigned int)R128_READ( R128_PM4_BUFFER_DL_WPTR ) );
+	printk( "PM4_BUFFER_DL_RPTR = 0x%08x\n",
+		(unsigned int)R128_READ( R128_PM4_BUFFER_DL_RPTR ) );
+	printk( "PM4_MICRO_CNTL     = 0x%08x\n",
+		(unsigned int)R128_READ( R128_PM4_MICRO_CNTL ) );
+	printk( "PM4_BUFFER_CNTL    = 0x%08x\n",
+		(unsigned int)R128_READ( R128_PM4_BUFFER_CNTL ) );
+}
+#endif
+
+
+/* ================================================================
+ * Engine, FIFO control
+ */
+
+static int r128_do_pixcache_flush( drm_r128_private_t *dev_priv )
+{
+	u32 tmp;
+	int i;
+
+	tmp = R128_READ( R128_PC_NGUI_CTLSTAT ) | R128_PC_FLUSH_ALL;
+	R128_WRITE( R128_PC_NGUI_CTLSTAT, tmp );
+
+	for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) {
+		if ( !(R128_READ( R128_PC_NGUI_CTLSTAT ) & R128_PC_BUSY) ) {
+			return 0;
+		}
+		udelay( 1 );
+	}
+
+	DRM_ERROR( "%s failed!\n", __FUNCTION__ );
+	return -EBUSY;
+}
+
+static int r128_do_wait_for_fifo( drm_r128_private_t *dev_priv, int entries )
+{
+	int i;
+
+	for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) {
+		int slots = R128_READ( R128_GUI_STAT ) & R128_GUI_FIFOCNT_MASK;
+		if ( slots >= entries ) return 0;
+		udelay( 1 );
+	}
+
+	DRM_ERROR( "%s failed!\n", __FUNCTION__ );
+	return -EBUSY;
+}
+
+static int r128_do_wait_for_idle( drm_r128_private_t *dev_priv )
+{
+	int i, ret;
+
+	ret = r128_do_wait_for_fifo( dev_priv, 64 );
+	if ( !ret ) return ret;
+
+	for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) {
+		if ( !(R128_READ( R128_GUI_STAT ) & R128_GUI_ACTIVE) ) {
+			r128_do_pixcache_flush( dev_priv );
+			return 0;
+		}
+		udelay( 1 );
+	}
+
+	DRM_ERROR( "%s failed!\n", __FUNCTION__ );
+	return -EBUSY;
+}
+
+
+/* ================================================================
+ * CCE control, initialization
+ */
+
+/* Load the microcode for the CCE */
+static void r128_cce_load_microcode( drm_r128_private_t *dev_priv )
+{
+	int i;
+
+	r128_do_wait_for_idle( dev_priv );
+
+	R128_WRITE( R128_PM4_MICROCODE_ADDR, 0 );
+	for ( i = 0 ; i < 256 ; i++ ) {
+		R128_WRITE( R128_PM4_MICROCODE_DATAH,
+			    r128_cce_microcode[i * 2] );
+		R128_WRITE( R128_PM4_MICROCODE_DATAL,
+			    r128_cce_microcode[i * 2 + 1] );
+	}
+}
+
+/* Flush any pending commands to the CCE.  This should only be used just
+ * prior to a wait for idle, as it informs the engine that the command
+ * stream is ending.
+ */
+static void r128_do_cce_flush( drm_r128_private_t *dev_priv )
+{
+	u32 tmp;
+
+	tmp = R128_READ( R128_PM4_BUFFER_DL_WPTR ) | R128_PM4_BUFFER_DL_DONE;
+	R128_WRITE( R128_PM4_BUFFER_DL_WPTR, tmp );
+}
+
+/* Wait for the CCE to go idle.
+ */
+static int r128_do_cce_idle( drm_r128_private_t *dev_priv )
+{
+	int i;
+
+	for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) {
+		if ( *dev_priv->ring.head == dev_priv->ring.tail ) {
+			int pm4stat = R128_READ( R128_PM4_STAT );
+			if ( ( (pm4stat & R128_PM4_FIFOCNT_MASK) >=
+			       dev_priv->cce_fifo_size ) &&
+			     !(pm4stat & (R128_PM4_BUSY |
+					  R128_PM4_GUI_ACTIVE)) ) {
+				return r128_do_pixcache_flush( dev_priv );
+			}
+		}
+		udelay( 1 );
+	}
+
+#if 0
+	DRM_ERROR( "failed!\n" );
+	r128_status( dev_priv );
+#endif
+	return -EBUSY;
+}
+
+/* Start the Concurrent Command Engine.
+ */
+static void r128_do_cce_start( drm_r128_private_t *dev_priv )
+{
+	r128_do_wait_for_idle( dev_priv );
+
+	R128_WRITE( R128_PM4_BUFFER_CNTL,
+		    dev_priv->cce_mode | dev_priv->ring.size_l2qw );
+	R128_READ( R128_PM4_BUFFER_ADDR ); /* as per the sample code */
+	R128_WRITE( R128_PM4_MICRO_CNTL, R128_PM4_MICRO_FREERUN );
+
+	dev_priv->cce_running = 1;
+}
+
+/* Reset the Concurrent Command Engine.  This will not flush any pending
+ * commangs, so you must wait for the CCE command stream to complete
+ * before calling this routine.
+ */
+static void r128_do_cce_reset( drm_r128_private_t *dev_priv )
+{
+	R128_WRITE( R128_PM4_BUFFER_DL_WPTR, 0 );
+	R128_WRITE( R128_PM4_BUFFER_DL_RPTR, 0 );
+	*dev_priv->ring.head = 0;
+	dev_priv->ring.tail = 0;
+}
+
+/* Stop the Concurrent Command Engine.  This will not flush any pending
+ * commangs, so you must flush the command stream and wait for the CCE
+ * to go idle before calling this routine.
+ */
+static void r128_do_cce_stop( drm_r128_private_t *dev_priv )
+{
+	R128_WRITE( R128_PM4_MICRO_CNTL, 0 );
+	R128_WRITE( R128_PM4_BUFFER_CNTL, R128_PM4_NONPM4 );
+
+	dev_priv->cce_running = 0;
+}
+
+/* Reset the engine.  This will stop the CCE if it is running.
+ */
+static int r128_do_engine_reset( drm_device_t *dev )
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	u32 clock_cntl_index, mclk_cntl, gen_reset_cntl;
+
+	r128_do_pixcache_flush( dev_priv );
+
+	clock_cntl_index = R128_READ( R128_CLOCK_CNTL_INDEX );
+	mclk_cntl = R128_READ_PLL( dev, R128_MCLK_CNTL );
+
+	R128_WRITE_PLL( R128_MCLK_CNTL,
+			mclk_cntl | R128_FORCE_GCP | R128_FORCE_PIPE3D_CP );
+
+	gen_reset_cntl = R128_READ( R128_GEN_RESET_CNTL );
+
+	/* Taken from the sample code - do not change */
+	R128_WRITE( R128_GEN_RESET_CNTL,
+		    gen_reset_cntl | R128_SOFT_RESET_GUI );
+	R128_READ( R128_GEN_RESET_CNTL );
+	R128_WRITE( R128_GEN_RESET_CNTL,
+		    gen_reset_cntl & ~R128_SOFT_RESET_GUI );
+	R128_READ( R128_GEN_RESET_CNTL );
+
+	R128_WRITE_PLL( R128_MCLK_CNTL, mclk_cntl );
+	R128_WRITE( R128_CLOCK_CNTL_INDEX, clock_cntl_index );
+	R128_WRITE( R128_GEN_RESET_CNTL, gen_reset_cntl );
+
+	/* Reset the CCE ring */
+	r128_do_cce_reset( dev_priv );
+
+	/* The CCE is no longer running after an engine reset */
+	dev_priv->cce_running = 0;
+
+	/* Reset any pending vertex, indirect buffers */
+	r128_freelist_reset( dev );
+
+	return 0;
+}
+
+static void r128_cce_init_ring_buffer( drm_device_t *dev )
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	u32 ring_start;
+	u32 tmp;
+
+	/* The manual (p. 2) says this address is in "VM space".  This
+	 * means it's an offset from the start of AGP space.
+	 */
+	ring_start = dev_priv->cce_ring->offset - dev->agp->base;
+	R128_WRITE( R128_PM4_BUFFER_OFFSET, ring_start | R128_AGP_OFFSET );
+
+	R128_WRITE( R128_PM4_BUFFER_DL_WPTR, 0 );
+	R128_WRITE( R128_PM4_BUFFER_DL_RPTR, 0 );
+
+	/* DL_RPTR_ADDR is a physical address in AGP space. */
+	*dev_priv->ring.head = 0;
+	R128_WRITE( R128_PM4_BUFFER_DL_RPTR_ADDR,
+		    dev_priv->ring_rptr->offset );
+
+	/* Set watermark control */
+	R128_WRITE( R128_PM4_BUFFER_WM_CNTL,
+		    ((R128_WATERMARK_L/4) << R128_WMA_SHIFT)
+		    | ((R128_WATERMARK_M/4) << R128_WMB_SHIFT)
+		    | ((R128_WATERMARK_N/4) << R128_WMC_SHIFT)
+		    | ((R128_WATERMARK_K/64) << R128_WB_WM_SHIFT) );
+
+	/* Force read.  Why?  Because it's in the examples... */
+	R128_READ( R128_PM4_BUFFER_ADDR );
+
+	/* Turn on bus mastering */
+	tmp = R128_READ( R128_BUS_CNTL ) & ~R128_BUS_MASTER_DIS;
+	R128_WRITE( R128_BUS_CNTL, tmp );
+}
+
+static int r128_do_init_cce( drm_device_t *dev, drm_r128_init_t *init )
+{
+	drm_r128_private_t *dev_priv;
+        int i;
+
+	dev_priv = drm_alloc( sizeof(drm_r128_private_t), DRM_MEM_DRIVER );
+	if ( dev_priv == NULL )
+		return -ENOMEM;
+	dev->dev_private = (void *)dev_priv;
+
+	memset( dev_priv, 0, sizeof(drm_r128_private_t) );
+
+	dev_priv->is_pci = init->is_pci;
+
+	/* GH: We don't support PCI cards until PCI GART is implemented.
+	 * Fail here so we can remove all checks for PCI cards around
+	 * the CCE ring code.
+	 */
+	if ( dev_priv->is_pci ) {
+		drm_free( dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER );
+		dev->dev_private = NULL;
+		return -EINVAL;
+	}
+
+	dev_priv->usec_timeout = init->usec_timeout;
+	if ( dev_priv->usec_timeout < 1 ||
+	     dev_priv->usec_timeout > R128_MAX_USEC_TIMEOUT ) {
+		drm_free( dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER );
+		dev->dev_private = NULL;
+		return -EINVAL;
+	}
+
+	dev_priv->cce_mode = init->cce_mode;
+	dev_priv->cce_secure = init->cce_secure;
+
+	/* GH: Simple idle check.
+	 */
+	atomic_set( &dev_priv->idle_count, 0 );
+
+	/* We don't support anything other than bus-mastering ring mode,
+	 * but the ring can be in either AGP or PCI space for the ring
+	 * read pointer.
+	 */
+	if ( ( init->cce_mode != R128_PM4_192BM ) &&
+	     ( init->cce_mode != R128_PM4_128BM_64INDBM ) &&
+	     ( init->cce_mode != R128_PM4_64BM_128INDBM ) &&
+	     ( init->cce_mode != R128_PM4_64BM_64VCBM_64INDBM ) ) {
+		drm_free( dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER );
+		dev->dev_private = NULL;
+		return -EINVAL;
+	}
+
+	switch ( init->cce_mode ) {
+	case R128_PM4_NONPM4:
+		dev_priv->cce_fifo_size = 0;
+		break;
+	case R128_PM4_192PIO:
+	case R128_PM4_192BM:
+		dev_priv->cce_fifo_size = 192;
+		break;
+	case R128_PM4_128PIO_64INDBM:
+	case R128_PM4_128BM_64INDBM:
+		dev_priv->cce_fifo_size = 128;
+		break;
+	case R128_PM4_64PIO_128INDBM:
+	case R128_PM4_64BM_128INDBM:
+	case R128_PM4_64PIO_64VCBM_64INDBM:
+	case R128_PM4_64BM_64VCBM_64INDBM:
+	case R128_PM4_64PIO_64VCPIO_64INDPIO:
+		dev_priv->cce_fifo_size = 64;
+		break;
+	}
+
+	dev_priv->fb_bpp	= init->fb_bpp;
+	dev_priv->front_offset	= init->front_offset;
+	dev_priv->front_pitch	= init->front_pitch;
+	dev_priv->back_offset	= init->back_offset;
+	dev_priv->back_pitch	= init->back_pitch;
+
+	dev_priv->depth_bpp	= init->depth_bpp;
+	dev_priv->depth_offset	= init->depth_offset;
+	dev_priv->depth_pitch	= init->depth_pitch;
+	dev_priv->span_offset	= init->span_offset;
+
+	dev_priv->front_pitch_offset_c = (((dev_priv->front_pitch/8) << 21) |
+					  (dev_priv->front_offset >> 5));
+	dev_priv->back_pitch_offset_c = (((dev_priv->back_pitch/8) << 21) |
+					 (dev_priv->back_offset >> 5));
+	dev_priv->depth_pitch_offset_c = (((dev_priv->depth_pitch/8) << 21) |
+					  (dev_priv->depth_offset >> 5) |
+					  R128_DST_TILE);
+	dev_priv->span_pitch_offset_c = (((dev_priv->depth_pitch/8) << 21) |
+					 (dev_priv->span_offset >> 5));
+
+	/* FIXME: We want multiple shared areas, including one shared
+	 * only by the X Server and kernel module.
+	 */
+	for ( i = 0 ; i < dev->map_count ; i++ ) {
+		if ( dev->maplist[i]->type == _DRM_SHM ) {
+			dev_priv->sarea = dev->maplist[i];
+			break;
+		}
+	}
+
+	DO_FIND_MAP( dev_priv->fb, init->fb_offset );
+	DO_FIND_MAP( dev_priv->mmio, init->mmio_offset );
+	DO_FIND_MAP( dev_priv->cce_ring, init->ring_offset );
+	DO_FIND_MAP( dev_priv->ring_rptr, init->ring_rptr_offset );
+	DO_FIND_MAP( dev_priv->buffers, init->buffers_offset );
+
+	if ( !dev_priv->is_pci ) {
+		DO_FIND_MAP( dev_priv->agp_textures,
+			     init->agp_textures_offset );
+	}
+
+	dev_priv->sarea_priv =
+		(drm_r128_sarea_t *)((u8 *)dev_priv->sarea->handle +
+				     init->sarea_priv_offset);
+
+	DO_REMAP( dev_priv->cce_ring );
+	DO_REMAP( dev_priv->ring_rptr );
+	DO_REMAP( dev_priv->buffers );
+#if 0
+	if ( !dev_priv->is_pci ) {
+		DO_REMAP( dev_priv->agp_textures );
+	}
+#endif
+
+	dev_priv->ring.head = ((__volatile__ u32 *)
+			       dev_priv->ring_rptr->handle);
+
+	dev_priv->ring.start = (u32 *)dev_priv->cce_ring->handle;
+	dev_priv->ring.end = ((u32 *)dev_priv->cce_ring->handle
+			      + init->ring_size / sizeof(u32));
+	dev_priv->ring.size = init->ring_size;
+	dev_priv->ring.size_l2qw = drm_order( init->ring_size / 8 );
+
+	dev_priv->ring.tail_mask =
+		(dev_priv->ring.size / sizeof(u32)) - 1;
+
+	dev_priv->sarea_priv->last_frame = 0;
+	R128_WRITE( R128_LAST_FRAME_REG, dev_priv->sarea_priv->last_frame );
+
+	dev_priv->sarea_priv->last_dispatch = 0;
+	R128_WRITE( R128_LAST_DISPATCH_REG,
+		    dev_priv->sarea_priv->last_dispatch );
+
+	r128_cce_init_ring_buffer( dev );
+	r128_cce_load_microcode( dev_priv );
+	r128_do_engine_reset( dev );
+
+	return 0;
+}
+
+static int r128_do_cleanup_cce( drm_device_t *dev )
+{
+	if ( dev->dev_private ) {
+		drm_r128_private_t *dev_priv = dev->dev_private;
+
+		DO_REMAPFREE( dev_priv->cce_ring );
+		DO_REMAPFREE( dev_priv->ring_rptr );
+		DO_REMAPFREE( dev_priv->buffers );
+#if 0
+		if ( !dev_priv->is_pci ) {
+			DO_REMAPFREE( dev_priv->agp_textures );
+		}
+#endif
+
+		drm_free( dev->dev_private, sizeof(drm_r128_private_t),
+			  DRM_MEM_DRIVER );
+		dev->dev_private = NULL;
+	}
+
+	return 0;
+}
+
+int r128_cce_init( struct inode *inode, struct file *filp,
+		   unsigned int cmd, unsigned long arg )
+{
+        drm_file_t *priv = filp->private_data;
+        drm_device_t *dev = priv->dev;
+	drm_r128_init_t init;
+
+	if ( copy_from_user( &init, (drm_r128_init_t *)arg, sizeof(init) ) )
+		return -EFAULT;
+
+	switch ( init.func ) {
+	case R128_INIT_CCE:
+		return r128_do_init_cce( dev, &init );
+	case R128_CLEANUP_CCE:
+		return r128_do_cleanup_cce( dev );
+	}
+
+	return -EINVAL;
+}
+
+int r128_cce_start( struct inode *inode, struct file *filp,
+		    unsigned int cmd, unsigned long arg )
+{
+        drm_file_t *priv = filp->private_data;
+        drm_device_t *dev = priv->dev;
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+	if ( dev_priv->cce_running || dev_priv->cce_mode == R128_PM4_NONPM4 ) {
+		DRM_DEBUG( "%s while CCE running\n", __FUNCTION__ );
+		return 0;
+	}
+
+	r128_do_cce_start( dev_priv );
+
+	return 0;
+}
+
+/* Stop the CCE.  The engine must have been idled before calling this
+ * routine.
+ */
+int r128_cce_stop( struct inode *inode, struct file *filp,
+		   unsigned int cmd, unsigned long arg )
+{
+        drm_file_t *priv = filp->private_data;
+        drm_device_t *dev = priv->dev;
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	drm_r128_cce_stop_t stop;
+	int ret;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &stop, (drm_r128_init_t *)arg, sizeof(stop) ) )
+		return -EFAULT;
+
+	/* Flush any pending CCE commands.  This ensures any outstanding
+	 * commands are exectuted by the engine before we turn it off.
+	 */
+	if ( stop.flush ) {
+		r128_do_cce_flush( dev_priv );
+	}
+
+	/* If we fail to make the engine go idle, we return an error
+	 * code so that the DRM ioctl wrapper can try again.
+	 */
+	if ( stop.idle ) {
+		ret = r128_do_cce_idle( dev_priv );
+		if ( ret < 0 ) return ret;
+	}
+
+	/* Finally, we can turn off the CCE.  If the engine isn't idle,
+	 * we will get some dropped triangles as they won't be fully
+	 * rendered before the CCE is shut down.
+	 */
+	r128_do_cce_stop( dev_priv );
+
+	/* Reset the engine */
+	r128_do_engine_reset( dev );
+
+	return 0;
+}
+
+/* Just reset the CCE ring.  Called as part of an X Server engine reset.
+ */
+int r128_cce_reset( struct inode *inode, struct file *filp,
+		    unsigned int cmd, unsigned long arg )
+{
+        drm_file_t *priv = filp->private_data;
+        drm_device_t *dev = priv->dev;
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+	if ( !dev_priv ) {
+		DRM_DEBUG( "%s called before init done\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	r128_do_cce_reset( dev_priv );
+
+	/* The CCE is no longer running after an engine reset */
+	dev_priv->cce_running = 0;
+
+	return 0;
+}
+
+int r128_cce_idle( struct inode *inode, struct file *filp,
+		   unsigned int cmd, unsigned long arg )
+{
+        drm_file_t *priv = filp->private_data;
+        drm_device_t *dev = priv->dev;
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( dev_priv->cce_running ) {
+		r128_do_cce_flush( dev_priv );
+	}
+
+	return r128_do_cce_idle( dev_priv );
+}
+
+int r128_engine_reset( struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg )
+{
+        drm_file_t *priv = filp->private_data;
+        drm_device_t *dev = priv->dev;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	return r128_do_engine_reset( dev );
+}
+
+
+/* ================================================================
+ * Freelist management
+ */
+#define R128_BUFFER_USED	0xffffffff
+#define R128_BUFFER_FREE	0
+
+#if 0
+static int r128_freelist_init( drm_device_t *dev )
+{
+	drm_device_dma_t *dma = dev->dma;
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	drm_buf_t *buf;
+	drm_r128_buf_priv_t *buf_priv;
+	drm_r128_freelist_t *entry;
+	int i;
+
+	dev_priv->head = drm_alloc( sizeof(drm_r128_freelist_t),
+				    DRM_MEM_DRIVER );
+	if ( dev_priv->head == NULL )
+		return -ENOMEM;
+
+	memset( dev_priv->head, 0, sizeof(drm_r128_freelist_t) );
+	dev_priv->head->age = R128_BUFFER_USED;
+
+	for ( i = 0 ; i < dma->buf_count ; i++ ) {
+		buf = dma->buflist[i];
+		buf_priv = buf->dev_private;
+
+		entry = drm_alloc( sizeof(drm_r128_freelist_t),
+				   DRM_MEM_DRIVER );
+		if ( !entry ) return -ENOMEM;
+
+		entry->age = R128_BUFFER_FREE;
+		entry->buf = buf;
+		entry->prev = dev_priv->head;
+		entry->next = dev_priv->head->next;
+		if ( !entry->next )
+			dev_priv->tail = entry;
+
+		buf_priv->discard = 0;
+		buf_priv->dispatched = 0;
+		buf_priv->list_entry = entry;
+
+		dev_priv->head->next = entry;
+
+		if ( dev_priv->head->next )
+			dev_priv->head->next->prev = entry;
+	}
+
+	return 0;
+
+}
+#endif
+
+drm_buf_t *r128_freelist_get( drm_device_t *dev )
+{
+	drm_device_dma_t *dma = dev->dma;
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	drm_r128_buf_priv_t *buf_priv;
+	drm_buf_t *buf;
+	int i, t;
+
+	/* FIXME: Optimize -- use freelist code */
+
+	for ( i = 0 ; i < dma->buf_count ; i++ ) {
+		buf = dma->buflist[i];
+		buf_priv = buf->dev_private;
+		if ( buf->pid == 0 )
+			return buf;
+	}
+
+	for ( t = 0 ; t < dev_priv->usec_timeout ; t++ ) {
+		u32 done_age = R128_READ( R128_LAST_DISPATCH_REG );
+
+		for ( i = 0 ; i < dma->buf_count ; i++ ) {
+			buf = dma->buflist[i];
+			buf_priv = buf->dev_private;
+			if ( buf->pending && buf_priv->age <= done_age ) {
+				/* The buffer has been processed, so it
+				 * can now be used.
+				 */
+				buf->pending = 0;
+				return buf;
+			}
+		}
+		udelay( 1 );
+	}
+
+	DRM_ERROR( "returning NULL!\n" );
+	return NULL;
+}
+
+void r128_freelist_reset( drm_device_t *dev )
+{
+	drm_device_dma_t *dma = dev->dma;
+	int i;
+
+	for ( i = 0 ; i < dma->buf_count ; i++ ) {
+		drm_buf_t *buf = dma->buflist[i];
+		drm_r128_buf_priv_t *buf_priv = buf->dev_private;
+		buf_priv->age = 0;
+	}
+}
+
+
+/* ================================================================
+ * CCE packet submission
+ */
+
+int r128_wait_ring( drm_r128_private_t *dev_priv, int n )
+{
+	drm_r128_ring_buffer_t *ring = &dev_priv->ring;
+	int i;
+
+	for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) {
+		ring->space = *ring->head - ring->tail;
+		if ( ring->space <= 0 )
+			ring->space += ring->size;
+
+		if ( ring->space >= n )
+			return 0;
+
+		udelay( 1 );
+	}
+
+	return -EBUSY;
+}
+
+void r128_update_ring_snapshot( drm_r128_private_t *dev_priv )
+{
+	drm_r128_ring_buffer_t *ring = &dev_priv->ring;
+
+	ring->space = *ring->head - ring->tail;
+#if R128_PERFORMANCE_BOXES
+	if ( ring->space == 0 )
+		atomic_inc( &dev_priv->idle_count );
+#endif
+	if ( ring->space <= 0 )
+		ring->space += ring->size;
+}
+
+#if 0
+static int r128_verify_command( drm_r128_private_t *dev_priv,
+				u32 cmd, int *size )
+{
+	int writing = 1;
+
+	*size = 0;
+
+	switch ( cmd & R128_CCE_PACKET_MASK ) {
+	case R128_CCE_PACKET0:
+		if ( (cmd & R128_CCE_PACKET0_REG_MASK) <= (0x1004 >> 2) &&
+		     (cmd & R128_CCE_PACKET0_REG_MASK) !=
+		     (R128_PM4_VC_FPU_SETUP >> 2) ) {
+			writing = 0;
+		}
+		*size = ((cmd & R128_CCE_PACKET_COUNT_MASK) >> 16) + 2;
+		break;
+
+	case R128_CCE_PACKET1:
+		if ( (cmd & R128_CCE_PACKET1_REG0_MASK) <= (0x1004 >> 2) &&
+		     (cmd & R128_CCE_PACKET1_REG0_MASK) !=
+		     (R128_PM4_VC_FPU_SETUP >> 2) ) {
+			writing = 0;
+		}
+		if ( (cmd & R128_CCE_PACKET1_REG1_MASK) <= (0x1004 << 9) &&
+		     (cmd & R128_CCE_PACKET1_REG1_MASK) !=
+		     (R128_PM4_VC_FPU_SETUP << 9) ) {
+			writing = 0;
+		}
+		*size = 3;
+		break;
+
+	case R128_CCE_PACKET2:
+		break;
+
+	case R128_CCE_PACKET3:
+		*size = ((cmd & R128_CCE_PACKET_COUNT_MASK) >> 16) + 2;
+		break;
+
+	}
+
+	return writing;
+}
+
+static int r128_submit_packet_ring_secure( drm_r128_private_t *dev_priv,
+					   u32 *commands, int *count )
+{
+#if 0
+	int write = dev_priv->sarea_priv->ring_write;
+	int *write_ptr = dev_priv->ring_start + write;
+	int c = *count;
+	u32 tmp = 0;
+	int psize = 0;
+	int writing = 1;
+	int timeout;
+
+	while ( c > 0 ) {
+		tmp = *commands++;
+		if ( !psize ) {
+			writing = r128_verify_command( dev_priv, tmp, &psize );
+		}
+		psize--;
+
+		if ( writing ) {
+			write++;
+			*write_ptr++ = tmp;
+		}
+		if ( write >= dev_priv->ring_entries ) {
+			write = 0;
+			write_ptr = dev_priv->ring_start;
+		}
+		timeout = 0;
+		while ( write == *dev_priv->ring_read_ptr ) {
+			R128_READ( R128_PM4_BUFFER_DL_RPTR );
+			if ( timeout++ >= dev_priv->usec_timeout )
+				return -EBUSY;
+			udelay( 1 );
+		}
+		c--;
+	}
+
+	if ( write < 32 ) {
+		memcpy( dev_priv->ring_end,
+			dev_priv->ring_start,
+			write * sizeof(u32) );
+	}
+
+	/* Make sure WC cache has been flushed */
+	r128_flush_write_combine();
+
+	dev_priv->sarea_priv->ring_write = write;
+	R128_WRITE( R128_PM4_BUFFER_DL_WPTR, write );
+
+	*count = 0;
+#endif
+	return 0;
+}
+
+static int r128_submit_packet_ring_insecure( drm_r128_private_t *dev_priv,
+					     u32 *commands, int *count )
+{
+#if 0
+	int write = dev_priv->sarea_priv->ring_write;
+	int *write_ptr = dev_priv->ring_start + write;
+	int c = *count;
+	int timeout;
+
+	while ( c > 0 ) {
+		write++;
+		*write_ptr++ = *commands++;
+		if ( write >= dev_priv->ring_entries ) {
+			write = 0;
+			write_ptr = dev_priv->ring_start;
+		}
+
+		timeout = 0;
+		while ( write == *dev_priv->ring_read_ptr ) {
+			R128_READ( R128_PM4_BUFFER_DL_RPTR );
+			if ( timeout++ >= dev_priv->usec_timeout )
+				return -EBUSY;
+			udelay( 1 );
+		}
+		c--;
+	}
+
+	if ( write < 32 ) {
+		memcpy( dev_priv->ring_end,
+			dev_priv->ring_start,
+			write * sizeof(u32) );
+	}
+
+	/* Make sure WC cache has been flushed */
+	r128_flush_write_combine();
+
+	dev_priv->sarea_priv->ring_write = write;
+	R128_WRITE( R128_PM4_BUFFER_DL_WPTR, write );
+
+	*count = 0;
+#endif
+	return 0;
+}
+#endif
+
+/* Internal packet submission routine.  This uses the insecure versions
+ * of the packet submission functions, and thus should only be used for
+ * packets generated inside the kernel module.
+ */
+int r128_do_submit_packet( drm_r128_private_t *dev_priv,
+			   u32 *buffer, int count )
+{
+	int c = count;
+	int ret = 0;
+
+#if 0
+	int left = 0;
+
+	if ( c >= dev_priv->ring_entries ) {
+		c = dev_priv->ring_entries - 1;
+		left = count - c;
+	}
+
+	/* Since this is only used by the kernel we can use the
+	 * insecure ring buffer submit packet routine.
+	 */
+	ret = r128_submit_packet_ring_insecure( dev_priv, buffer, &c );
+	c += left;
+#endif
+
+	return ( ret < 0 ) ? ret : c;
+}
+
+/* External packet submission routine.  This uses the secure versions
+ * by default, and can thus submit packets received from user space.
+ */
+int r128_cce_packet( struct inode *inode, struct file *filp,
+		     unsigned int cmd, unsigned long arg )
+{
+        drm_file_t *priv = filp->private_data;
+        drm_device_t *dev = priv->dev;
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	drm_r128_packet_t packet;
+	u32 *buffer;
+	int c;
+	int size;
+	int ret = 0;
+
+#if 0
+	/* GH: Disable packet submission for now.
+	 */
+	return -EINVAL;
+#endif
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "r128_submit_packet called without lock held\n" );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &packet, (drm_r128_packet_t *)arg,
+			     sizeof(packet) ) )
+		return -EFAULT;
+
+#if 0
+	c = packet.count;
+	size = c * sizeof(*buffer);
+
+	{
+		int left = 0;
+
+		if ( c >= dev_priv->ring_entries ) {
+			c = dev_priv->ring_entries - 1;
+			size = c * sizeof(*buffer);
+			left = packet.count - c;
+		}
+
+		buffer = kmalloc( size, 0 );
+		if ( buffer == NULL)
+			return -ENOMEM;
+		if ( copy_from_user( buffer, packet.buffer, size ) )
+			return -EFAULT;
+
+		if ( dev_priv->cce_secure ) {
+			ret = r128_submit_packet_ring_secure( dev_priv,
+							      buffer, &c );
+		} else {
+			ret = r128_submit_packet_ring_insecure( dev_priv,
+								buffer, &c );
+		}
+		c += left;
+	}
+
+	kfree( buffer );
+#else
+	c = 0;
+#endif
+
+	packet.count = c;
+	if ( copy_to_user( (drm_r128_packet_t *)arg, &packet,
+			   sizeof(packet) ) )
+		return -EFAULT;
+
+	if ( ret ) {
+		return ret;
+	} else if ( c > 0 ) {
+		return -EAGAIN;
+	}
+	return 0;
+}
+
+#if 0
+static int r128_send_vertbufs( drm_device_t *dev, drm_r128_vertex_t *v )
+{
+	drm_device_dma_t    *dma      = dev->dma;
+	drm_r128_private_t  *dev_priv = dev->dev_private;
+	drm_r128_buf_priv_t *buf_priv;
+	drm_buf_t           *buf;
+	int                  i, ret;
+	RING_LOCALS;
+
+	/* Make sure we have valid data */
+	for (i = 0; i < v->send_count; i++) {
+		int idx = v->send_indices[i];
+
+		if (idx < 0 || idx >= dma->buf_count) {
+			DRM_ERROR("Index %d (of %d max)\n",
+				  idx, dma->buf_count - 1);
+			return -EINVAL;
+		}
+		buf = dma->buflist[idx];
+		if (buf->pid != current->pid) {
+			DRM_ERROR("Process %d using buffer owned by %d\n",
+				  current->pid, buf->pid);
+			return -EINVAL;
+		}
+		if (buf->pending) {
+			DRM_ERROR("Sending pending buffer:"
+				  " buffer %d, offset %d\n",
+				  v->send_indices[i], i);
+			return -EINVAL;
+		}
+	}
+
+	/* Wait for idle, if we've wrapped to make sure that all pending
+           buffers have been processed */
+	if (dev_priv->submit_age == R128_MAX_VBUF_AGE) {
+		if ((ret = r128_do_cce_idle(dev)) < 0) return ret;
+		dev_priv->submit_age = 0;
+		r128_freelist_reset(dev);
+	}
+
+	/* Make sure WC cache has been flushed (if in PIO mode) */
+	if (!dev_priv->cce_is_bm_mode) r128_flush_write_combine();
+
+	/* FIXME: Add support for sending vertex buffer to the CCE here
+	   instead of in client code.  The v->prim holds the primitive
+	   type that should be drawn.  Loop over the list buffers in
+	   send_indices[] and submit a packet for each VB.
+
+	   This will require us to loop over the clip rects here as
+	   well, which implies that we extend the kernel driver to allow
+	   cliprects to be stored here.  Note that the cliprects could
+	   possibly come from the X server instead of the client, but
+	   this will require additional changes to the DRI to allow for
+	   this optimization. */
+
+	/* Submit a CCE packet that writes submit_age to R128_VB_AGE_REG */
+#if 0
+	cce_buffer[0] = R128CCE0(R128_CCE_PACKET0, R128_VB_AGE_REG, 0);
+	cce_buffer[1] = dev_priv->submit_age;
+
+	if ((ret = r128_do_submit_packet(dev, cce_buffer, 2)) < 0) {
+		/* Until we add support for sending VBs to the CCE in
+		   this routine, we can recover from this error.  After
+		   we add that support, we won't be able to easily
+		   recover, so we will probably have to implement
+		   another mechanism for handling timeouts from packets
+		   submitted directly by the kernel. */
+		return ret;
+	}
+#else
+	BEGIN_RING( 2 );
+
+	OUT_RING( CCE_PACKET0( R128_VB_AGE_REG, 0 ) );
+	OUT_RING( dev_priv->submit_age );
+
+	ADVANCE_RING();
+#endif
+	/* Now that the submit packet request has succeeded, we can mark
+           the buffers as pending */
+	for (i = 0; i < v->send_count; i++) {
+		buf = dma->buflist[v->send_indices[i]];
+		buf->pending = 1;
+
+		buf_priv      = buf->dev_private;
+		buf_priv->age = dev_priv->submit_age;
+	}
+
+	dev_priv->submit_age++;
+
+	return 0;
+}
+#endif
+
+
+
+
+static int r128_cce_get_buffers( drm_device_t *dev, drm_dma_t *d )
+{
+	int i;
+	drm_buf_t *buf;
+
+	for ( i = d->granted_count ; i < d->request_count ; i++ ) {
+		buf = r128_freelist_get( dev );
+		if ( !buf ) return -EAGAIN;
+
+		buf->pid = current->pid;
+
+		if ( copy_to_user( &d->request_indices[i], &buf->idx,
+				   sizeof(buf->idx) ) )
+			return -EFAULT;
+		if ( copy_to_user( &d->request_sizes[i], &buf->total,
+				   sizeof(buf->total) ) )
+			return -EFAULT;
+
+		d->granted_count++;
+	}
+	return 0;
+}
+
+int r128_cce_buffers( struct inode *inode, struct file *filp,
+		      unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_device_dma_t *dma = dev->dma;
+	int ret = 0;
+	drm_dma_t d;
+
+	if ( copy_from_user( &d, (drm_dma_t *) arg, sizeof(d) ) )
+		return -EFAULT;
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	/* Please don't send us buffers.
+	 */
+	if ( d.send_count != 0 ) {
+		DRM_ERROR( "Process %d trying to send %d buffers via drmDMA\n",
+			   current->pid, d.send_count );
+		return -EINVAL;
+	}
+
+	/* We'll send you buffers.
+	 */
+	if ( d.request_count < 0 || d.request_count > dma->buf_count ) {
+		DRM_ERROR( "Process %d trying to get %d buffers (of %d max)\n",
+			   current->pid, d.request_count, dma->buf_count );
+		return -EINVAL;
+	}
+
+	d.granted_count = 0;
+
+	if ( d.request_count ) {
+		ret = r128_cce_get_buffers( dev, &d );
+	}
+
+	if ( copy_to_user( (drm_dma_t *) arg, &d, sizeof(d) ) )
+		return -EFAULT;
+
+	return ret;
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/r128_context.c linux/drivers/char/drm-4.0/r128_context.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/r128_context.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/r128_context.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,217 @@
+/* r128_context.c -- IOCTLs for r128 contexts -*- linux-c -*-
+ * Created: Mon Dec 13 09:51:35 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Author: Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "r128_drv.h"
+
+extern drm_ctx_t r128_res_ctx;
+
+static int r128_alloc_queue(drm_device_t *dev)
+{
+	return drm_ctxbitmap_next(dev);
+}
+
+int r128_context_switch(drm_device_t *dev, int old, int new)
+{
+        char        buf[64];
+
+        atomic_inc(&dev->total_ctx);
+
+        if (test_and_set_bit(0, &dev->context_flag)) {
+                DRM_ERROR("Reentering -- FIXME\n");
+                return -EBUSY;
+        }
+
+#if DRM_DMA_HISTOGRAM
+        dev->ctx_start = get_cycles();
+#endif
+
+        DRM_DEBUG("Context switch from %d to %d\n", old, new);
+
+        if (new == dev->last_context) {
+                clear_bit(0, &dev->context_flag);
+                return 0;
+        }
+
+        if (drm_flags & DRM_FLAG_NOCTX) {
+                r128_context_switch_complete(dev, new);
+        } else {
+                sprintf(buf, "C %d %d\n", old, new);
+                drm_write_string(dev, buf);
+        }
+
+        return 0;
+}
+
+int r128_context_switch_complete(drm_device_t *dev, int new)
+{
+        dev->last_context = new;  /* PRE/POST: This is the _only_ writer. */
+        dev->last_switch  = jiffies;
+
+        if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+                DRM_ERROR("Lock isn't held after context switch\n");
+        }
+
+				/* If a context switch is ever initiated
+                                   when the kernel holds the lock, release
+                                   that lock here. */
+#if DRM_DMA_HISTOGRAM
+        atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles()
+                                                      - dev->ctx_start)]);
+
+#endif
+        clear_bit(0, &dev->context_flag);
+        wake_up(&dev->context_wait);
+
+        return 0;
+}
+
+
+int r128_resctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_ctx_res_t	res;
+	drm_ctx_t	ctx;
+	int		i;
+
+	DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS);
+	if (copy_from_user(&res, (drm_ctx_res_t *)arg, sizeof(res)))
+		return -EFAULT;
+	if (res.count >= DRM_RESERVED_CONTEXTS) {
+		memset(&ctx, 0, sizeof(ctx));
+		for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
+			ctx.handle = i;
+			if (copy_to_user(&res.contexts[i],
+					 &i,
+					 sizeof(i)))
+				return -EFAULT;
+		}
+	}
+	res.count = DRM_RESERVED_CONTEXTS;
+	if (copy_to_user((drm_ctx_res_t *)arg, &res, sizeof(res)))
+		return -EFAULT;
+	return 0;
+}
+
+
+int r128_addctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	if ((ctx.handle = r128_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) {
+				/* Skip kernel's context and get a new one. */
+		ctx.handle = r128_alloc_queue(dev);
+	}
+	DRM_DEBUG("%d\n", ctx.handle);
+	if (ctx.handle == -1) {
+		DRM_DEBUG("Not enough free contexts.\n");
+				/* Should this return -EBUSY instead? */
+		return -ENOMEM;
+	}
+
+	if (copy_to_user((drm_ctx_t *)arg, &ctx, sizeof(ctx)))
+		return -EFAULT;
+	return 0;
+}
+
+int r128_modctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	unsigned long arg)
+{
+	drm_ctx_t ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx)))
+		return -EFAULT;
+	if (ctx.flags==_DRM_CONTEXT_PRESERVED)
+		r128_res_ctx.handle=ctx.handle;
+	return 0;
+}
+
+int r128_getctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	unsigned long arg)
+{
+	drm_ctx_t ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx)))
+		return -EFAULT;
+	/* This is 0, because we don't hanlde any context flags */
+	ctx.flags = 0;
+	if (copy_to_user((drm_ctx_t*)arg, &ctx, sizeof(ctx)))
+		return -EFAULT;
+	return 0;
+}
+
+int r128_switchctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		   unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	return r128_context_switch(dev, dev->last_context, ctx.handle);
+}
+
+int r128_newctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	r128_context_switch_complete(dev, ctx.handle);
+
+	return 0;
+}
+
+int r128_rmctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	drm_ctxbitmap_free(dev, ctx.handle);
+
+	return 0;
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/r128_drm.h linux/drivers/char/drm-4.0/r128_drm.h
--- linux-2.4.18-pre2/drivers/char/drm-4.0/r128_drm.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/r128_drm.h	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,272 @@
+/* r128_drm.h -- Public header for the r128 driver -*- linux-c -*-
+ * Created: Wed Apr  5 19:24:19 2000 by kevin@precisioninsight.com
+ *
+ * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Kevin E. Martin <martin@valinux.com>
+ *    Gareth Hughes <gareth@valinux.com>
+ *
+ */
+
+#ifndef _R128_DRM_H_
+#define _R128_DRM_H_
+
+/* WARNING: If you change any of these defines, make sure to change the
+ * defines in the X server file (r128_sarea.h)
+ */
+#ifndef __R128_SAREA_DEFINES__
+#define __R128_SAREA_DEFINES__
+
+/* What needs to be changed for the current vertex buffer?
+ */
+#define R128_UPLOAD_CONTEXT		0x001
+#define R128_UPLOAD_SETUP		0x002
+#define R128_UPLOAD_TEX0		0x004
+#define R128_UPLOAD_TEX1		0x008
+#define R128_UPLOAD_TEX0IMAGES		0x010
+#define R128_UPLOAD_TEX1IMAGES		0x020
+#define R128_UPLOAD_CORE		0x040
+#define R128_UPLOAD_MASKS		0x080
+#define R128_UPLOAD_WINDOW		0x100
+#define R128_UPLOAD_CLIPRECTS		0x200	/* handled client-side */
+#define R128_REQUIRE_QUIESCENCE		0x400
+#define R128_UPLOAD_ALL			0x7ff
+
+#define R128_FRONT			0x1
+#define R128_BACK			0x2
+#define R128_DEPTH			0x4
+
+/* Primitive types
+ */
+#define R128_POINTS			0x1
+#define R128_LINES			0x2
+#define R128_LINE_STRIP			0x3
+#define R128_TRIANGLES			0x4
+#define R128_TRIANGLE_FAN		0x5
+#define R128_TRIANGLE_STRIP		0x6
+
+/* Vertex/indirect buffer size
+ */
+#if 1
+#define R128_BUFFER_SIZE		16384
+#else
+#define R128_BUFFER_SIZE		(128 * 1024)
+#endif
+
+/* Byte offsets for indirect buffer data
+ */
+#define R128_INDEX_PRIM_OFFSET		20
+#define R128_HOSTDATA_BLIT_OFFSET	32
+
+/* 2048x2048 @ 32bpp texture requires this many indirect buffers
+ */
+#define R128_MAX_BLIT_BUFFERS		((2048 * 2048 * 4) / R128_BUFFER_SIZE)
+
+/* Keep these small for testing.
+ */
+#define R128_NR_SAREA_CLIPRECTS		12
+
+/* There are 2 heaps (local/AGP).  Each region within a heap is a
+ *  minimum of 64k, and there are at most 64 of them per heap.
+ */
+#define R128_LOCAL_TEX_HEAP		0
+#define R128_AGP_TEX_HEAP		1
+#define R128_NR_TEX_HEAPS		2
+#define R128_NR_TEX_REGIONS		64
+#define R128_LOG_TEX_GRANULARITY	16
+
+#define R128_NR_CONTEXT_REGS		12
+#define R128_TEX_MAXLEVELS		11
+
+#endif /* __R128_SAREA_DEFINES__ */
+
+typedef struct {
+	/* Context state - can be written in one large chunk */
+	unsigned int dst_pitch_offset_c;
+	unsigned int dp_gui_master_cntl_c;
+	unsigned int sc_top_left_c;
+	unsigned int sc_bottom_right_c;
+	unsigned int z_offset_c;
+	unsigned int z_pitch_c;
+	unsigned int z_sten_cntl_c;
+	unsigned int tex_cntl_c;
+	unsigned int misc_3d_state_cntl_reg;
+	unsigned int texture_clr_cmp_clr_c;
+	unsigned int texture_clr_cmp_msk_c;
+	unsigned int fog_color_c;
+
+	/* Texture state */
+	unsigned int tex_size_pitch_c;
+	unsigned int constant_color_c;
+
+	/* Setup state */
+	unsigned int pm4_vc_fpu_setup;
+	unsigned int setup_cntl;
+
+	/* Mask state */
+	unsigned int dp_write_mask;
+	unsigned int sten_ref_mask_c;
+	unsigned int plane_3d_mask_c;
+
+	/* Window state */
+	unsigned int window_xy_offset;
+
+	/* Core state */
+	unsigned int scale_3d_cntl;
+} drm_r128_context_regs_t;
+
+/* Setup registers for each texture unit */
+typedef struct {
+	unsigned int tex_cntl;
+	unsigned int tex_combine_cntl;
+	unsigned int tex_size_pitch;
+	unsigned int tex_offset[R128_TEX_MAXLEVELS];
+	unsigned int tex_border_color;
+} drm_r128_texture_regs_t;
+
+
+typedef struct drm_tex_region {
+	unsigned char next, prev;
+	unsigned char in_use;
+	int age;
+} drm_tex_region_t;
+
+typedef struct drm_r128_sarea {
+	/* The channel for communication of state information to the kernel
+	 * on firing a vertex buffer.
+	 */
+	drm_r128_context_regs_t context_state;
+	drm_r128_texture_regs_t tex_state[R128_NR_TEX_HEAPS];
+	unsigned int dirty;
+	unsigned int vertsize;
+	unsigned int vc_format;
+
+	/* The current cliprects, or a subset thereof.
+	 */
+	drm_clip_rect_t boxes[R128_NR_SAREA_CLIPRECTS];
+	unsigned int nbox;
+
+	/* Counters for client-side throttling of rendering clients.
+	 */
+	unsigned int last_frame;
+	unsigned int last_dispatch;
+
+	drm_tex_region_t tex_list[R128_NR_TEX_HEAPS][R128_NR_TEX_REGIONS+1];
+	int tex_age[R128_NR_TEX_HEAPS];
+	int ctx_owner;
+} drm_r128_sarea_t;
+
+
+/* WARNING: If you change any of these defines, make sure to change the
+ * defines in the Xserver file (xf86drmR128.h)
+ */
+typedef struct drm_r128_init {
+	enum {
+		R128_INIT_CCE    = 0x01,
+		R128_CLEANUP_CCE = 0x02
+	} func;
+	int sarea_priv_offset;
+	int is_pci;
+	int cce_mode;
+	int cce_secure;
+	int ring_size;
+	int usec_timeout;
+
+	unsigned int fb_bpp;
+	unsigned int front_offset, front_pitch;
+	unsigned int back_offset, back_pitch;
+	unsigned int depth_bpp;
+	unsigned int depth_offset, depth_pitch;
+	unsigned int span_offset;
+
+	unsigned int fb_offset;
+	unsigned int mmio_offset;
+	unsigned int ring_offset;
+	unsigned int ring_rptr_offset;
+	unsigned int buffers_offset;
+	unsigned int agp_textures_offset;
+} drm_r128_init_t;
+
+typedef struct drm_r128_cce_stop {
+	int flush;
+	int idle;
+} drm_r128_cce_stop_t;
+
+typedef struct drm_r128_clear {
+	unsigned int flags;
+	int x, y, w, h;
+	unsigned int clear_color;
+	unsigned int clear_depth;
+} drm_r128_clear_t;
+
+typedef struct drm_r128_vertex {
+	int prim;
+	int idx;			/* Index of vertex buffer */
+	int count;			/* Number of vertices in buffer */
+	int discard;			/* Client finished with buffer? */
+} drm_r128_vertex_t;
+
+typedef struct drm_r128_indices {
+	int prim;
+	int idx;
+	int start;
+	int end;
+	int discard;			/* Client finished with buffer? */
+} drm_r128_indices_t;
+
+typedef struct drm_r128_blit {
+	int idx;
+	int pitch;
+	int offset;
+	int format;
+	unsigned short x, y;
+	unsigned short width, height;
+} drm_r128_blit_t;
+
+typedef struct drm_r128_depth {
+	enum {
+		R128_WRITE_SPAN		= 0x01,
+		R128_WRITE_PIXELS	= 0x02,
+		R128_READ_SPAN		= 0x03,
+		R128_READ_PIXELS	= 0x04
+	} func;
+	int n;
+	int *x;
+	int *y;
+	unsigned int *buffer;
+	unsigned char *mask;
+} drm_r128_depth_t;
+
+typedef struct drm_r128_stipple {
+	unsigned int *mask;
+} drm_r128_stipple_t;
+
+typedef struct drm_r128_packet {
+	unsigned int *buffer;
+	int count;
+	int flags;
+} drm_r128_packet_t;
+
+#endif
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/r128_drv.c linux/drivers/char/drm-4.0/r128_drv.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/r128_drv.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/r128_drv.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,700 @@
+/* r128_drv.c -- ATI Rage 128 driver -*- linux-c -*-
+ * Created: Mon Dec 13 09:47:27 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *    Kevin E. Martin <martin@valinux.com>
+ *    Gareth Hughes <gareth@valinux.com>
+ *
+ */
+
+#include <linux/config.h>
+#include "drmP.h"
+#include "r128_drv.h"
+
+#define R128_NAME		"r128"
+#define R128_DESC		"ATI Rage 128"
+#define R128_DATE		"20001215"
+#define R128_MAJOR		2
+#define R128_MINOR		1
+#define R128_PATCHLEVEL		2
+
+static drm_device_t	r128_device;
+drm_ctx_t		r128_res_ctx;
+
+static struct file_operations r128_fops = {
+#if LINUX_VERSION_CODE >= 0x020400
+				/* This started being used during 2.4.0-test */
+	owner:   THIS_MODULE,
+#endif
+	open:	 r128_open,
+	flush:	 drm_flush,
+	release: r128_release,
+	ioctl:	 r128_ioctl,
+	mmap:	 drm_mmap,
+	read:	 drm_read,
+	fasync:	 drm_fasync,
+	poll:	 drm_poll,
+};
+
+static struct miscdevice      r128_misc = {
+	minor: MISC_DYNAMIC_MINOR,
+	name:  R128_NAME,
+	fops:  &r128_fops,
+};
+
+static drm_ioctl_desc_t	      r128_ioctls[] = {
+	[DRM_IOCTL_NR(DRM_IOCTL_VERSION)]      = { r128_version,      0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)]   = { drm_getunique,     0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)]    = { drm_getmagic,      0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)]    = { drm_irq_busid,     0, 1 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)]   = { drm_setunique,     1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_BLOCK)]	       = { drm_block,         1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)]      = { drm_unblock,       1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)]   = { drm_authmagic,     1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)]      = { drm_addmap,        1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)]     = { r128_addbufs,      1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)]    = { drm_markbufs,      1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)]    = { drm_infobufs,      1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MAP_BUFS)]     = { r128_mapbufs,      1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)]    = { drm_freebufs,      1, 0 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)]      = { r128_addctx,	      1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)]       = { r128_rmctx,	      1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)]      = { r128_modctx,	      1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)]      = { r128_getctx,	      1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)]   = { r128_switchctx,    1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)]      = { r128_newctx,	      1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)]      = { r128_resctx,	      1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)]     = { drm_adddraw,	      1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)]      = { drm_rmdraw,	      1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_DMA)]	       = { r128_cce_buffers,  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_LOCK)]	       = { r128_lock,	      1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)]       = { r128_unlock,	      1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FINISH)]       = { drm_finish,	      1, 0 },
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)]  = { drm_agp_acquire,   1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)]  = { drm_agp_release,   1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)]   = { drm_agp_enable,    1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)]     = { drm_agp_info,      1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)]    = { drm_agp_alloc,     1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)]     = { drm_agp_free,      1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)]     = { drm_agp_bind,      1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)]   = { drm_agp_unbind,    1, 1 },
+#endif
+
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_INIT)]    = { r128_cce_init,     1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_CCE_START)] = { r128_cce_start,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_CCE_STOP)]  = { r128_cce_stop,   1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_CCE_RESET)] = { r128_cce_reset,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_CCE_IDLE)]  = { r128_cce_idle,   1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_RESET)]   = { r128_engine_reset, 1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_SWAP)]    = { r128_cce_swap,     1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_CLEAR)]   = { r128_cce_clear,    1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_VERTEX)]  = { r128_cce_vertex,   1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_INDICES)] = { r128_cce_indices,  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_BLIT)]    = { r128_cce_blit,     1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_DEPTH)]   = { r128_cce_depth,    1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_STIPPLE)] = { r128_cce_stipple,  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_PACKET)]  = { r128_cce_packet,   1, 0 },
+};
+#define R128_IOCTL_COUNT DRM_ARRAY_SIZE(r128_ioctls)
+
+#ifdef MODULE
+static char		      *r128 = NULL;
+#endif
+
+MODULE_AUTHOR("VA Linux Systems, Inc.");
+MODULE_DESCRIPTION("r128");
+MODULE_LICENSE("GPL and additional rights");
+MODULE_PARM(r128, "s");
+
+#ifndef MODULE
+/* r128_options is called by the kernel to parse command-line options
+ * passed via the boot-loader (e.g., LILO).  It calls the insmod option
+ * routine, drm_parse_drm.
+ */
+
+static int __init r128_options(char *str)
+{
+	drm_parse_options(str);
+	return 1;
+}
+
+__setup("r128=", r128_options);
+#endif
+
+static int r128_setup(drm_device_t *dev)
+{
+	int i;
+
+	atomic_set(&dev->ioctl_count, 0);
+	atomic_set(&dev->vma_count, 0);
+	dev->buf_use	  = 0;
+	atomic_set(&dev->buf_alloc, 0);
+
+	drm_dma_setup(dev);
+
+	atomic_set(&dev->total_open, 0);
+	atomic_set(&dev->total_close, 0);
+	atomic_set(&dev->total_ioctl, 0);
+	atomic_set(&dev->total_irq, 0);
+	atomic_set(&dev->total_ctx, 0);
+	atomic_set(&dev->total_locks, 0);
+	atomic_set(&dev->total_unlocks, 0);
+	atomic_set(&dev->total_contends, 0);
+	atomic_set(&dev->total_sleeps, 0);
+
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		dev->magiclist[i].head = NULL;
+		dev->magiclist[i].tail = NULL;
+	}
+	dev->maplist	    = NULL;
+	dev->map_count	    = 0;
+	dev->vmalist	    = NULL;
+	dev->lock.hw_lock   = NULL;
+	init_waitqueue_head(&dev->lock.lock_queue);
+	dev->queue_count    = 0;
+	dev->queue_reserved = 0;
+	dev->queue_slots    = 0;
+	dev->queuelist	    = NULL;
+	dev->irq	    = 0;
+	dev->context_flag   = 0;
+	dev->interrupt_flag = 0;
+	dev->dma_flag	    = 0;
+	dev->last_context   = 0;
+	dev->last_switch    = 0;
+	dev->last_checked   = 0;
+	init_timer(&dev->timer);
+	init_waitqueue_head(&dev->context_wait);
+
+	dev->ctx_start	    = 0;
+	dev->lck_start	    = 0;
+
+	dev->buf_rp	  = dev->buf;
+	dev->buf_wp	  = dev->buf;
+	dev->buf_end	  = dev->buf + DRM_BSZ;
+	dev->buf_async	  = NULL;
+	init_waitqueue_head(&dev->buf_readers);
+	init_waitqueue_head(&dev->buf_writers);
+
+	r128_res_ctx.handle=-1;
+
+	DRM_DEBUG("\n");
+
+	/* The kernel's context could be created here, but is now created
+	   in drm_dma_enqueue.	This is more resource-efficient for
+	   hardware that does not do DMA, but may mean that
+	   drm_select_queue fails between the time the interrupt is
+	   initialized and the time the queues are initialized. */
+
+	return 0;
+}
+
+
+static int r128_takedown(drm_device_t *dev)
+{
+	int		  i;
+	drm_magic_entry_t *pt, *next;
+	drm_map_t	  *map;
+	drm_vma_entry_t	  *vma, *vma_next;
+
+	DRM_DEBUG("\n");
+
+	down(&dev->struct_sem);
+	del_timer(&dev->timer);
+
+	if (dev->devname) {
+		drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER);
+		dev->devname = NULL;
+	}
+
+	if (dev->unique) {
+		drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER);
+		dev->unique = NULL;
+		dev->unique_len = 0;
+	}
+				/* Clear pid list */
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		for (pt = dev->magiclist[i].head; pt; pt = next) {
+			next = pt->next;
+			drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
+		}
+		dev->magiclist[i].head = dev->magiclist[i].tail = NULL;
+	}
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+				/* Clear AGP information */
+	if (dev->agp) {
+		drm_agp_mem_t *entry;
+		drm_agp_mem_t *nexte;
+
+				/* Remove AGP resources, but leave dev->agp
+                                   intact until r128_cleanup is called. */
+		for (entry = dev->agp->memory; entry; entry = nexte) {
+			nexte = entry->next;
+			if (entry->bound) drm_unbind_agp(entry->memory);
+			drm_free_agp(entry->memory, entry->pages);
+			drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS);
+		}
+		dev->agp->memory = NULL;
+
+		if (dev->agp->acquired)	_drm_agp_release();
+
+		dev->agp->acquired = 0;
+		dev->agp->enabled  = 0;
+	}
+#endif
+
+				/* Clear vma list (only built for debugging) */
+	if (dev->vmalist) {
+		for (vma = dev->vmalist; vma; vma = vma_next) {
+			vma_next = vma->next;
+			drm_free(vma, sizeof(*vma), DRM_MEM_VMAS);
+		}
+		dev->vmalist = NULL;
+	}
+
+				/* Clear map area and mtrr information */
+	if (dev->maplist) {
+		for (i = 0; i < dev->map_count; i++) {
+			map = dev->maplist[i];
+			switch (map->type) {
+			case _DRM_REGISTERS:
+			case _DRM_FRAME_BUFFER:
+#ifdef CONFIG_MTRR
+				if (map->mtrr >= 0) {
+					int retcode;
+					retcode = mtrr_del(map->mtrr,
+							   map->offset,
+							   map->size);
+					DRM_DEBUG("mtrr_del = %d\n", retcode);
+				}
+#endif
+				drm_ioremapfree(map->handle, map->size);
+				break;
+			case _DRM_SHM:
+				drm_free_pages((unsigned long)map->handle,
+					       drm_order(map->size)
+					       - PAGE_SHIFT,
+					       DRM_MEM_SAREA);
+				break;
+			case _DRM_AGP:
+				/* Do nothing here, because this is all
+                                   handled in the AGP/GART driver. */
+				break;
+			}
+			drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+		}
+		drm_free(dev->maplist,
+			 dev->map_count * sizeof(*dev->maplist),
+			 DRM_MEM_MAPS);
+		dev->maplist   = NULL;
+		dev->map_count = 0;
+	}
+
+	drm_dma_takedown(dev);
+
+	dev->queue_count     = 0;
+	if (dev->lock.hw_lock) {
+		dev->lock.hw_lock    = NULL; /* SHM removed */
+		dev->lock.pid	     = 0;
+		wake_up_interruptible(&dev->lock.lock_queue);
+	}
+	up(&dev->struct_sem);
+
+	return 0;
+}
+
+/* r128_init is called via init_module at module load time, or via
+ * linux/init/main.c (this is not currently supported). */
+
+static int __init r128_init(void)
+{
+	int		      retcode;
+	drm_device_t	      *dev = &r128_device;
+
+	DRM_DEBUG("\n");
+
+	memset((void *)dev, 0, sizeof(*dev));
+	dev->count_lock	  = SPIN_LOCK_UNLOCKED;
+	sema_init(&dev->struct_sem, 1);
+
+#ifdef MODULE
+	drm_parse_options(r128);
+#endif
+
+	if ((retcode = misc_register(&r128_misc))) {
+		DRM_ERROR("Cannot register \"%s\"\n", R128_NAME);
+		return retcode;
+	}
+	dev->device = MKDEV(MISC_MAJOR, r128_misc.minor);
+	dev->name   = R128_NAME;
+
+	drm_mem_init();
+	drm_proc_init(dev);
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	dev->agp    = drm_agp_init();
+	if (dev->agp == NULL) {
+		DRM_ERROR("Cannot initialize agpgart module.\n");
+		drm_proc_cleanup();
+		misc_deregister(&r128_misc);
+		r128_takedown(dev);
+		return -ENOMEM;
+	}
+
+#ifdef CONFIG_MTRR
+	dev->agp->agp_mtrr = mtrr_add(dev->agp->agp_info.aper_base,
+				      dev->agp->agp_info.aper_size*1024*1024,
+				      MTRR_TYPE_WRCOMB,
+				      1);
+#endif
+#endif
+
+	if((retcode = drm_ctxbitmap_init(dev))) {
+		DRM_ERROR("Cannot allocate memory for context bitmap.\n");
+		drm_proc_cleanup();
+		misc_deregister(&r128_misc);
+		r128_takedown(dev);
+		return retcode;
+	}
+
+	DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
+		 R128_NAME,
+		 R128_MAJOR,
+		 R128_MINOR,
+		 R128_PATCHLEVEL,
+		 R128_DATE,
+		 r128_misc.minor);
+
+	return 0;
+}
+
+/* r128_cleanup is called via cleanup_module at module unload time. */
+
+static void __exit r128_cleanup(void)
+{
+	drm_device_t	      *dev = &r128_device;
+
+	DRM_DEBUG("\n");
+
+	drm_proc_cleanup();
+	if (misc_deregister(&r128_misc)) {
+		DRM_ERROR("Cannot unload module\n");
+	} else {
+		DRM_INFO("Module unloaded\n");
+	}
+	drm_ctxbitmap_cleanup(dev);
+	r128_takedown(dev);
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	if (dev->agp) {
+		drm_agp_uninit();
+		drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS);
+		dev->agp = NULL;
+	}
+#endif
+}
+
+module_init(r128_init);
+module_exit(r128_cleanup);
+
+
+int r128_version(struct inode *inode, struct file *filp,
+		 unsigned int cmd, unsigned long arg)
+{
+	drm_version_t version;
+	int	      len;
+
+	if (copy_from_user(&version,
+			   (drm_version_t *)arg,
+			   sizeof(version)))
+		return -EFAULT;
+
+#define DRM_COPY(name,value)					\
+	len = strlen(value);					\
+	if (len > name##_len) len = name##_len;			\
+	name##_len = strlen(value);				\
+	if (len && name) {					\
+		if (copy_to_user(name, value, len))		\
+			return -EFAULT;				\
+	}
+
+	version.version_major	   = R128_MAJOR;
+	version.version_minor	   = R128_MINOR;
+	version.version_patchlevel = R128_PATCHLEVEL;
+
+	DRM_COPY(version.name, R128_NAME);
+	DRM_COPY(version.date, R128_DATE);
+	DRM_COPY(version.desc, R128_DESC);
+
+	if (copy_to_user((drm_version_t *)arg,
+			 &version,
+			 sizeof(version)))
+		return -EFAULT;
+	return 0;
+}
+
+int r128_open(struct inode *inode, struct file *filp)
+{
+	drm_device_t  *dev    = &r128_device;
+	int	      retcode = 0;
+
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	if (!(retcode = drm_open_helper(inode, filp, dev))) {
+#if LINUX_VERSION_CODE < 0x020333
+		MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+		atomic_inc(&dev->total_open);
+		spin_lock(&dev->count_lock);
+		if (!dev->open_count++) {
+			spin_unlock(&dev->count_lock);
+			return r128_setup(dev);
+		}
+		spin_unlock(&dev->count_lock);
+	}
+
+	return retcode;
+}
+
+int r128_release(struct inode *inode, struct file *filp)
+{
+	drm_file_t    *priv   = filp->private_data;
+	drm_device_t  *dev;
+	int	      retcode = 0;
+
+	lock_kernel();
+	dev = priv->dev;
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	if (!(retcode = drm_release(inode, filp))) {
+#if LINUX_VERSION_CODE < 0x020333
+		MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+		atomic_inc(&dev->total_close);
+		spin_lock(&dev->count_lock);
+		if (!--dev->open_count) {
+			if (atomic_read(&dev->ioctl_count) || dev->blocked) {
+				DRM_ERROR("Device busy: %d %d\n",
+					  atomic_read(&dev->ioctl_count),
+					  dev->blocked);
+				spin_unlock(&dev->count_lock);
+				unlock_kernel();
+				return -EBUSY;
+			}
+			spin_unlock(&dev->count_lock);
+			unlock_kernel();
+			return r128_takedown(dev);
+		}
+		spin_unlock(&dev->count_lock);
+	}
+
+	unlock_kernel();
+	return retcode;
+}
+
+/* r128_ioctl is called whenever a process performs an ioctl on /dev/drm. */
+int r128_ioctl(struct inode *inode, struct file *filp,
+	       unsigned int cmd, unsigned long arg)
+{
+	int		 nr	 = DRM_IOCTL_NR(cmd);
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	int		 retcode = 0;
+	drm_ioctl_desc_t *ioctl;
+	drm_ioctl_t	 *func;
+
+	atomic_inc(&dev->ioctl_count);
+	atomic_inc(&dev->total_ioctl);
+	++priv->ioctl_count;
+
+	DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n",
+		  current->pid, cmd, nr, dev->device, priv->authenticated);
+
+	if (nr >= R128_IOCTL_COUNT) {
+		retcode = -EINVAL;
+	} else {
+		ioctl	  = &r128_ioctls[nr];
+		func	  = ioctl->func;
+
+		if (!func) {
+			DRM_DEBUG("no function\n");
+			retcode = -EINVAL;
+		} else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN))
+			   || (ioctl->auth_needed && !priv->authenticated)) {
+			retcode = -EACCES;
+		} else {
+			retcode = (func)(inode, filp, cmd, arg);
+		}
+	}
+
+#if 0
+	if ( retcode ) {
+		DRM_INFO( "%s 0x%x ret = %d\n", __FUNCTION__, nr, retcode );
+	}
+#endif
+
+	atomic_dec(&dev->ioctl_count);
+	return retcode;
+}
+
+int r128_lock(struct inode *inode, struct file *filp,
+	      unsigned int cmd, unsigned long arg)
+{
+        drm_file_t        *priv   = filp->private_data;
+        drm_device_t      *dev    = priv->dev;
+        DECLARE_WAITQUEUE(entry, current);
+        int               ret   = 0;
+        drm_lock_t        lock;
+#if DRM_DMA_HISTOGRAM
+        cycles_t          start;
+
+        dev->lck_start = start = get_cycles();
+#endif
+
+        if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)))
+		return -EFAULT;
+
+        if (lock.context == DRM_KERNEL_CONTEXT) {
+                DRM_ERROR("Process %d using kernel context %d\n",
+                          current->pid, lock.context);
+                return -EINVAL;
+        }
+
+        DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
+                  lock.context, current->pid, dev->lock.hw_lock->lock,
+                  lock.flags);
+
+        if (lock.context < 0)
+                return -EINVAL;
+
+        if (!ret) {
+                add_wait_queue(&dev->lock.lock_queue, &entry);
+                for (;;) {
+                        current->state = TASK_INTERRUPTIBLE;
+                        if (!dev->lock.hw_lock) {
+                                /* Device has been unregistered */
+                                ret = -EINTR;
+                                break;
+                        }
+                        if (drm_lock_take(&dev->lock.hw_lock->lock,
+                                          lock.context)) {
+                                dev->lock.pid       = current->pid;
+                                dev->lock.lock_time = jiffies;
+                                atomic_inc(&dev->total_locks);
+                                break;  /* Got lock */
+                        }
+
+                                /* Contention */
+                        atomic_inc(&dev->total_sleeps);
+                        schedule();
+                        if (signal_pending(current)) {
+                                ret = -ERESTARTSYS;
+                                break;
+                        }
+                }
+                current->state = TASK_RUNNING;
+                remove_wait_queue(&dev->lock.lock_queue, &entry);
+        }
+
+        if (!ret) {
+		sigemptyset(&dev->sigmask);
+		sigaddset(&dev->sigmask, SIGSTOP);
+		sigaddset(&dev->sigmask, SIGTSTP);
+		sigaddset(&dev->sigmask, SIGTTIN);
+		sigaddset(&dev->sigmask, SIGTTOU);
+		dev->sigdata.context = lock.context;
+		dev->sigdata.lock    = dev->lock.hw_lock;
+		block_all_signals(drm_notifier, &dev->sigdata, &dev->sigmask);
+                if (lock.flags & _DRM_LOCK_READY) {
+				/* Wait for space in DMA/FIFO */
+		}
+                if (lock.flags & _DRM_LOCK_QUIESCENT) {
+				/* Make hardware quiescent */
+			DRM_DEBUG( "not quiescent!\n" );
+#if 0
+                        r128_quiescent(dev);
+#endif
+		}
+        }
+
+#if LINUX_VERSION_CODE < 0x020400
+	if (lock.context != r128_res_ctx.handle) {
+		current->counter = 5;
+		current->priority = DEF_PRIORITY/4;
+	}
+#endif
+        DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
+
+#if DRM_DMA_HISTOGRAM
+        atomic_inc(&dev->histo.lacq[drm_histogram_slot(get_cycles() - start)]);
+#endif
+
+        return ret;
+}
+
+
+int r128_unlock(struct inode *inode, struct file *filp,
+		unsigned int cmd, unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	drm_lock_t	  lock;
+
+	if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)))
+		return -EFAULT;
+
+	if (lock.context == DRM_KERNEL_CONTEXT) {
+		DRM_ERROR("Process %d using kernel context %d\n",
+			  current->pid, lock.context);
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("%d frees lock (%d holds)\n",
+		  lock.context,
+		  _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+	atomic_inc(&dev->total_unlocks);
+	if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock))
+		atomic_inc(&dev->total_contends);
+	drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT);
+				/* FIXME: Try to send data to card here */
+	if (!dev->context_flag) {
+		if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				  DRM_KERNEL_CONTEXT)) {
+			DRM_ERROR("\n");
+		}
+	}
+
+#if LINUX_VERSION_CODE < 0x020400
+	if (lock.context != r128_res_ctx.handle) {
+		current->counter = 5;
+		current->priority = DEF_PRIORITY;
+	}
+#endif
+	unblock_all_signals();
+	return 0;
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/r128_drv.h linux/drivers/char/drm-4.0/r128_drv.h
--- linux-2.4.18-pre2/drivers/char/drm-4.0/r128_drv.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/r128_drv.h	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,469 @@
+/* r128_drv.h -- Private header for r128 driver -*- linux-c -*-
+ * Created: Mon Dec 13 09:51:11 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Rickard E. (Rik) Faith <faith@valinux.com>
+ *   Kevin E. Martin <martin@valinux.com>
+ *   Gareth Hughes <gareth@valinux.com>
+ *
+ */
+
+#ifndef __R128_DRV_H__
+#define __R128_DRV_H__
+
+typedef struct drm_r128_freelist {
+   	unsigned int age;
+   	drm_buf_t *buf;
+   	struct drm_r128_freelist *next;
+   	struct drm_r128_freelist *prev;
+} drm_r128_freelist_t;
+
+typedef struct drm_r128_ring_buffer {
+	u32 *start;
+	u32 *end;
+	int size;
+	int size_l2qw;
+
+	volatile u32 *head;
+	u32 tail;
+	u32 tail_mask;
+	int space;
+} drm_r128_ring_buffer_t;
+
+typedef struct drm_r128_private {
+	drm_r128_ring_buffer_t ring;
+	drm_r128_sarea_t *sarea_priv;
+
+	int cce_mode;
+	int cce_fifo_size;
+	int cce_secure;
+	int cce_running;
+
+   	drm_r128_freelist_t *head;
+   	drm_r128_freelist_t *tail;
+
+	int usec_timeout;
+	int is_pci;
+
+	atomic_t idle_count;
+
+	unsigned int fb_bpp;
+	unsigned int front_offset;
+	unsigned int front_pitch;
+	unsigned int back_offset;
+	unsigned int back_pitch;
+
+	unsigned int depth_bpp;
+	unsigned int depth_offset;
+	unsigned int depth_pitch;
+	unsigned int span_offset;
+
+	u32 front_pitch_offset_c;
+	u32 back_pitch_offset_c;
+	u32 depth_pitch_offset_c;
+	u32 span_pitch_offset_c;
+
+	drm_map_t *sarea;
+	drm_map_t *fb;
+	drm_map_t *mmio;
+	drm_map_t *cce_ring;
+	drm_map_t *ring_rptr;
+	drm_map_t *buffers;
+	drm_map_t *agp_textures;
+} drm_r128_private_t;
+
+typedef struct drm_r128_buf_priv {
+	u32 age;
+	int prim;
+	int discard;
+	int dispatched;
+   	drm_r128_freelist_t *list_entry;
+} drm_r128_buf_priv_t;
+
+				/* r128_drv.c */
+extern int  r128_version( struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg );
+extern int  r128_open( struct inode *inode, struct file *filp );
+extern int  r128_release( struct inode *inode, struct file *filp );
+extern int  r128_ioctl( struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg );
+extern int  r128_lock( struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg );
+extern int  r128_unlock( struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg );
+
+				/* r128_cce.c */
+extern int r128_cce_init( struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg );
+extern int r128_cce_start( struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg );
+extern int r128_cce_stop( struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg );
+extern int r128_cce_reset( struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg );
+extern int r128_cce_idle( struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg );
+extern int r128_engine_reset( struct inode *inode, struct file *filp,
+			      unsigned int cmd, unsigned long arg );
+extern int r128_cce_packet( struct inode *inode, struct file *filp,
+			    unsigned int cmd, unsigned long arg );
+extern int r128_cce_buffers( struct inode *inode, struct file *filp,
+			     unsigned int cmd, unsigned long arg );
+
+extern void r128_freelist_reset( drm_device_t *dev );
+extern drm_buf_t *r128_freelist_get( drm_device_t *dev );
+
+extern int r128_wait_ring( drm_r128_private_t *dev_priv, int n );
+extern void r128_update_ring_snapshot( drm_r128_private_t *dev_priv );
+
+				/* r128_state.c */
+extern int r128_cce_clear( struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg );
+extern int r128_cce_swap( struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg );
+extern int r128_cce_vertex( struct inode *inode, struct file *filp,
+			    unsigned int cmd, unsigned long arg );
+extern int r128_cce_indices( struct inode *inode, struct file *filp,
+			     unsigned int cmd, unsigned long arg );
+extern int r128_cce_blit( struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg );
+extern int r128_cce_depth( struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg );
+extern int r128_cce_stipple( struct inode *inode, struct file *filp,
+			     unsigned int cmd, unsigned long arg );
+
+				/* r128_bufs.c */
+extern int r128_addbufs(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int r128_mapbufs(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+
+				/* r128_context.c */
+extern int  r128_resctx(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  r128_addctx(struct inode *inode, struct file *filp,
+		        unsigned int cmd, unsigned long arg);
+extern int  r128_modctx(struct inode *inode, struct file *filp,
+		        unsigned int cmd, unsigned long arg);
+extern int  r128_getctx(struct inode *inode, struct file *filp,
+		        unsigned int cmd, unsigned long arg);
+extern int  r128_switchctx(struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg);
+extern int  r128_newctx(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  r128_rmctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+
+extern int  r128_context_switch(drm_device_t *dev, int old, int new);
+extern int  r128_context_switch_complete(drm_device_t *dev, int new);
+
+
+/* Register definitions, register access macros and drmAddMap constants
+ * for Rage 128 kernel driver.
+ */
+
+#define R128_AUX_SC_CNTL		0x1660
+#	define R128_AUX1_SC_EN			(1 << 0)
+#	define R128_AUX1_SC_MODE_OR		(0 << 1)
+#	define R128_AUX1_SC_MODE_NAND		(1 << 1)
+#	define R128_AUX2_SC_EN			(1 << 2)
+#	define R128_AUX2_SC_MODE_OR		(0 << 3)
+#	define R128_AUX2_SC_MODE_NAND		(1 << 3)
+#	define R128_AUX3_SC_EN			(1 << 4)
+#	define R128_AUX3_SC_MODE_OR		(0 << 5)
+#	define R128_AUX3_SC_MODE_NAND		(1 << 5)
+#define R128_AUX1_SC_LEFT		0x1664
+#define R128_AUX1_SC_RIGHT		0x1668
+#define R128_AUX1_SC_TOP		0x166c
+#define R128_AUX1_SC_BOTTOM		0x1670
+#define R128_AUX2_SC_LEFT		0x1674
+#define R128_AUX2_SC_RIGHT		0x1678
+#define R128_AUX2_SC_TOP		0x167c
+#define R128_AUX2_SC_BOTTOM		0x1680
+#define R128_AUX3_SC_LEFT		0x1684
+#define R128_AUX3_SC_RIGHT		0x1688
+#define R128_AUX3_SC_TOP		0x168c
+#define R128_AUX3_SC_BOTTOM		0x1690
+
+#define R128_BRUSH_DATA0		0x1480
+#define R128_BUS_CNTL			0x0030
+#	define R128_BUS_MASTER_DIS		(1 << 6)
+
+#define R128_CLOCK_CNTL_INDEX		0x0008
+#define R128_CLOCK_CNTL_DATA		0x000c
+#	define R128_PLL_WR_EN			(1 << 7)
+
+#define R128_CONSTANT_COLOR_C		0x1d34
+
+#define R128_DP_GUI_MASTER_CNTL		0x146c
+#       define R128_GMC_SRC_PITCH_OFFSET_CNTL	(1    <<  0)
+#       define R128_GMC_DST_PITCH_OFFSET_CNTL	(1    <<  1)
+#	define R128_GMC_BRUSH_SOLID_COLOR	(13   <<  4)
+#	define R128_GMC_BRUSH_NONE		(15   <<  4)
+#	define R128_GMC_DST_16BPP		(4    <<  8)
+#	define R128_GMC_DST_24BPP		(5    <<  8)
+#	define R128_GMC_DST_32BPP		(6    <<  8)
+#       define R128_GMC_DST_DATATYPE_SHIFT	8
+#	define R128_GMC_SRC_DATATYPE_COLOR	(3    << 12)
+#	define R128_DP_SRC_SOURCE_MEMORY	(2    << 24)
+#	define R128_DP_SRC_SOURCE_HOST_DATA	(3    << 24)
+#	define R128_GMC_CLR_CMP_CNTL_DIS	(1    << 28)
+#	define R128_GMC_AUX_CLIP_DIS		(1    << 29)
+#	define R128_GMC_WR_MSK_DIS		(1    << 30)
+#	define R128_ROP3_S			0x00cc0000
+#	define R128_ROP3_P			0x00f00000
+#define R128_DP_WRITE_MASK		0x16cc
+#define R128_DST_PITCH_OFFSET_C		0x1c80
+#	define R128_DST_TILE			(1 << 31)
+
+#define R128_GEN_RESET_CNTL		0x00f0
+#	define R128_SOFT_RESET_GUI		(1 <<  0)
+
+#define R128_GUI_SCRATCH_REG0		0x15e0
+#define R128_GUI_SCRATCH_REG1		0x15e4
+#define R128_GUI_SCRATCH_REG2		0x15e8
+#define R128_GUI_SCRATCH_REG3		0x15ec
+#define R128_GUI_SCRATCH_REG4		0x15f0
+#define R128_GUI_SCRATCH_REG5		0x15f4
+
+#define R128_GUI_STAT			0x1740
+#	define R128_GUI_FIFOCNT_MASK		0x0fff
+#	define R128_GUI_ACTIVE			(1 << 31)
+
+#define R128_MCLK_CNTL			0x000f
+#	define R128_FORCE_GCP			(1 << 16)
+#	define R128_FORCE_PIPE3D_CP		(1 << 17)
+#	define R128_FORCE_RCP			(1 << 18)
+
+#define R128_PC_GUI_CTLSTAT		0x1748
+#define R128_PC_NGUI_CTLSTAT		0x0184
+#	define R128_PC_FLUSH_GUI		(3 << 0)
+#	define R128_PC_RI_GUI			(1 << 2)
+#	define R128_PC_FLUSH_ALL		0x00ff
+#	define R128_PC_BUSY			(1 << 31)
+
+#define R128_PRIM_TEX_CNTL_C		0x1cb0
+
+#define R128_SCALE_3D_CNTL		0x1a00
+#define R128_SEC_TEX_CNTL_C		0x1d00
+#define R128_SEC_TEXTURE_BORDER_COLOR_C	0x1d3c
+#define R128_SETUP_CNTL			0x1bc4
+#define R128_STEN_REF_MASK_C		0x1d40
+
+#define R128_TEX_CNTL_C			0x1c9c
+#	define R128_TEX_CACHE_FLUSH		(1 << 23)
+
+#define R128_WINDOW_XY_OFFSET		0x1bcc
+
+
+/* CCE registers
+ */
+#define R128_PM4_BUFFER_OFFSET		0x0700
+#define R128_PM4_BUFFER_CNTL		0x0704
+#	define R128_PM4_MASK			(15 << 28)
+#	define R128_PM4_NONPM4			(0  << 28)
+#	define R128_PM4_192PIO			(1  << 28)
+#	define R128_PM4_192BM			(2  << 28)
+#	define R128_PM4_128PIO_64INDBM		(3  << 28)
+#	define R128_PM4_128BM_64INDBM		(4  << 28)
+#	define R128_PM4_64PIO_128INDBM		(5  << 28)
+#	define R128_PM4_64BM_128INDBM		(6  << 28)
+#	define R128_PM4_64PIO_64VCBM_64INDBM	(7  << 28)
+#	define R128_PM4_64BM_64VCBM_64INDBM	(8  << 28)
+#	define R128_PM4_64PIO_64VCPIO_64INDPIO	(15 << 28)
+
+#define R128_PM4_BUFFER_WM_CNTL		0x0708
+#	define R128_WMA_SHIFT			0
+#	define R128_WMB_SHIFT			8
+#	define R128_WMC_SHIFT			16
+#	define R128_WB_WM_SHIFT			24
+
+#define R128_PM4_BUFFER_DL_RPTR_ADDR	0x070c
+#define R128_PM4_BUFFER_DL_RPTR		0x0710
+#define R128_PM4_BUFFER_DL_WPTR		0x0714
+#	define R128_PM4_BUFFER_DL_DONE		(1 << 31)
+
+#define R128_PM4_VC_FPU_SETUP		0x071c
+
+#define R128_PM4_IW_INDOFF		0x0738
+#define R128_PM4_IW_INDSIZE		0x073c
+
+#define R128_PM4_STAT			0x07b8
+#	define R128_PM4_FIFOCNT_MASK		0x0fff
+#	define R128_PM4_BUSY			(1 << 16)
+#	define R128_PM4_GUI_ACTIVE		(1 << 31)
+
+#define R128_PM4_MICROCODE_ADDR		0x07d4
+#define R128_PM4_MICROCODE_RADDR	0x07d8
+#define R128_PM4_MICROCODE_DATAH	0x07dc
+#define R128_PM4_MICROCODE_DATAL	0x07e0
+
+#define R128_PM4_BUFFER_ADDR		0x07f0
+#define R128_PM4_MICRO_CNTL		0x07fc
+#	define R128_PM4_MICRO_FREERUN		(1 << 30)
+
+#define R128_PM4_FIFO_DATA_EVEN		0x1000
+#define R128_PM4_FIFO_DATA_ODD		0x1004
+
+
+/* CCE command packets
+ */
+#define R128_CCE_PACKET0		0x00000000
+#define R128_CCE_PACKET1		0x40000000
+#define R128_CCE_PACKET2		0x80000000
+#define R128_CCE_PACKET3		0xC0000000
+#	define R128_CNTL_HOSTDATA_BLT		0x00009400
+#	define R128_CNTL_PAINT_MULTI		0x00009A00
+#	define R128_CNTL_BITBLT_MULTI		0x00009B00
+#	define R128_3D_RNDR_GEN_INDX_PRIM	0x00002300
+
+#define R128_CCE_PACKET_MASK		0xC0000000
+#define R128_CCE_PACKET_COUNT_MASK	0x3fff0000
+#define R128_CCE_PACKET0_REG_MASK	0x000007ff
+#define R128_CCE_PACKET1_REG0_MASK	0x000007ff
+#define R128_CCE_PACKET1_REG1_MASK	0x003ff800
+
+#define R128_CCE_VC_CNTL_PRIM_TYPE_NONE		0x00000000
+#define R128_CCE_VC_CNTL_PRIM_TYPE_POINT	0x00000001
+#define R128_CCE_VC_CNTL_PRIM_TYPE_LINE		0x00000002
+#define R128_CCE_VC_CNTL_PRIM_TYPE_POLY_LINE	0x00000003
+#define R128_CCE_VC_CNTL_PRIM_TYPE_TRI_LIST	0x00000004
+#define R128_CCE_VC_CNTL_PRIM_TYPE_TRI_FAN	0x00000005
+#define R128_CCE_VC_CNTL_PRIM_TYPE_TRI_STRIP	0x00000006
+#define R128_CCE_VC_CNTL_PRIM_TYPE_TRI_TYPE2	0x00000007
+#define R128_CCE_VC_CNTL_PRIM_WALK_IND		0x00000010
+#define R128_CCE_VC_CNTL_PRIM_WALK_LIST		0x00000020
+#define R128_CCE_VC_CNTL_PRIM_WALK_RING		0x00000030
+#define R128_CCE_VC_CNTL_NUM_SHIFT		16
+
+#define R128_DATATYPE_CI8		2
+#define R128_DATATYPE_ARGB1555		3
+#define R128_DATATYPE_RGB565		4
+#define R128_DATATYPE_RGB888		5
+#define R128_DATATYPE_ARGB8888		6
+#define R128_DATATYPE_RGB332		7
+#define R128_DATATYPE_RGB8		9
+#define R128_DATATYPE_ARGB4444		15
+
+/* Constants */
+#define R128_AGP_OFFSET			0x02000000
+
+#define R128_WATERMARK_L		16
+#define R128_WATERMARK_M		8
+#define R128_WATERMARK_N		8
+#define R128_WATERMARK_K		128
+
+#define R128_MAX_USEC_TIMEOUT	100000	/* 100 ms */
+
+#define R128_LAST_FRAME_REG		R128_GUI_SCRATCH_REG0
+#define R128_LAST_DISPATCH_REG		R128_GUI_SCRATCH_REG1
+#define R128_MAX_VB_AGE			0xffffffff
+
+#define R128_MAX_VB_VERTS		(0xffff)
+
+
+#define R128_BASE(reg)		((unsigned long)(dev_priv->mmio->handle))
+#define R128_ADDR(reg)		(R128_BASE(reg) + reg)
+
+#define R128_READ(reg)		readl(R128_ADDR(reg))
+#define R128_WRITE(reg,val)	writel(val,R128_ADDR(reg))
+
+#define R128_READ8(reg)		readb(R128_ADDR(reg))
+#define R128_WRITE8(reg,val)	writeb(val,R128_ADDR(reg))
+
+#define R128_WRITE_PLL(addr,val)                                              \
+do {                                                                          \
+	R128_WRITE8(R128_CLOCK_CNTL_INDEX, ((addr) & 0x1f) | R128_PLL_WR_EN); \
+	R128_WRITE(R128_CLOCK_CNTL_DATA, (val));                              \
+} while (0)
+
+extern int R128_READ_PLL(drm_device_t *dev, int addr);
+
+#define R128CCE0(p,r,n)   ((p) | ((n) << 16) | ((r) >> 2))
+#define R128CCE1(p,r1,r2) ((p) | (((r2) >> 2) << 11) | ((r1) >> 2))
+#define R128CCE2(p)       ((p))
+#define R128CCE3(p,n)     ((p) | ((n) << 16))
+
+
+
+
+#define CCE_PACKET0( reg, n )		(R128_CCE_PACKET0 |		\
+					 ((n) << 16) | ((reg) >> 2))
+#define CCE_PACKET1( reg0, reg1 )	(R128_CCE_PACKET1 |		\
+					 (((reg1) >> 2) << 11) | ((reg0) >> 2))
+#define CCE_PACKET2()			(R128_CCE_PACKET2)
+#define CCE_PACKET3( pkt, n )		(R128_CCE_PACKET3 |		\
+					 (pkt) | ((n) << 16))
+
+
+#define r128_flush_write_combine()		mb()
+
+
+#define R128_VERBOSE	0
+
+#define RING_LOCALS	int write; unsigned int tail_mask; volatile u32 *ring;
+
+#define BEGIN_RING( n ) do {						\
+	if ( R128_VERBOSE ) {						\
+		DRM_INFO( "BEGIN_RING( %d ) in %s\n",			\
+			   n, __FUNCTION__ );				\
+	}								\
+	if ( dev_priv->ring.space < n * sizeof(u32) ) {			\
+		r128_wait_ring( dev_priv, n * sizeof(u32) );		\
+	}								\
+	dev_priv->ring.space -= n * sizeof(u32);			\
+	ring = dev_priv->ring.start;					\
+	write = dev_priv->ring.tail;					\
+	tail_mask = dev_priv->ring.tail_mask;				\
+} while (0)
+
+#define ADVANCE_RING() do {						\
+	if ( R128_VERBOSE ) {						\
+		DRM_INFO( "ADVANCE_RING() tail=0x%06x wr=0x%06x\n",	\
+			  write, dev_priv->ring.tail );			\
+	}								\
+	if ( write < 32 ) {						\
+		memcpy( dev_priv->ring.end,				\
+			dev_priv->ring.start,				\
+			write * sizeof(u32) );				\
+	}								\
+	r128_flush_write_combine();					\
+	dev_priv->ring.tail = write;					\
+	R128_WRITE( R128_PM4_BUFFER_DL_WPTR, write );			\
+} while (0)
+
+#define OUT_RING( x ) do {						\
+	if ( R128_VERBOSE ) {						\
+		DRM_INFO( "   OUT_RING( 0x%08x ) at 0x%x\n",		\
+			   (unsigned int)(x), write );			\
+	}								\
+	ring[write++] = x;						\
+	write &= tail_mask;						\
+} while (0)
+
+#define R128_PERFORMANCE_BOXES	0
+
+#endif /* __R128_DRV_H__ */
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/r128_state.c linux/drivers/char/drm-4.0/r128_state.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/r128_state.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/r128_state.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,1605 @@
+/* r128_state.c -- State support for r128 -*- linux-c -*-
+ * Created: Thu Jan 27 02:53:43 2000 by gareth@valinux.com
+ *
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Gareth Hughes <gareth@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "r128_drv.h"
+#include "drm.h"
+
+
+/* ================================================================
+ * CCE hardware state programming functions
+ */
+
+static void r128_emit_clip_rects( drm_r128_private_t *dev_priv,
+				  drm_clip_rect_t *boxes, int count )
+{
+	u32 aux_sc_cntl = 0x00000000;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 17 );
+
+	if ( count >= 1 ) {
+		OUT_RING( CCE_PACKET0( R128_AUX1_SC_LEFT, 3 ) );
+		OUT_RING( boxes[0].x1 );
+		OUT_RING( boxes[0].x2 - 1 );
+		OUT_RING( boxes[0].y1 );
+		OUT_RING( boxes[0].y2 - 1 );
+
+		aux_sc_cntl |= (R128_AUX1_SC_EN | R128_AUX1_SC_MODE_OR);
+	}
+	if ( count >= 2 ) {
+		OUT_RING( CCE_PACKET0( R128_AUX2_SC_LEFT, 3 ) );
+		OUT_RING( boxes[1].x1 );
+		OUT_RING( boxes[1].x2 - 1 );
+		OUT_RING( boxes[1].y1 );
+		OUT_RING( boxes[1].y2 - 1 );
+
+		aux_sc_cntl |= (R128_AUX2_SC_EN | R128_AUX2_SC_MODE_OR);
+	}
+	if ( count >= 3 ) {
+		OUT_RING( CCE_PACKET0( R128_AUX3_SC_LEFT, 3 ) );
+		OUT_RING( boxes[2].x1 );
+		OUT_RING( boxes[2].x2 - 1 );
+		OUT_RING( boxes[2].y1 );
+		OUT_RING( boxes[2].y2 - 1 );
+
+		aux_sc_cntl |= (R128_AUX3_SC_EN | R128_AUX3_SC_MODE_OR);
+	}
+
+	OUT_RING( CCE_PACKET0( R128_AUX_SC_CNTL, 0 ) );
+	OUT_RING( aux_sc_cntl );
+
+	ADVANCE_RING();
+}
+
+static inline void r128_emit_core( drm_r128_private_t *dev_priv )
+{
+	drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_r128_context_regs_t *ctx = &sarea_priv->context_state;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 2 );
+
+	OUT_RING( CCE_PACKET0( R128_SCALE_3D_CNTL, 0 ) );
+	OUT_RING( ctx->scale_3d_cntl );
+
+	ADVANCE_RING();
+}
+
+static inline void r128_emit_context( drm_r128_private_t *dev_priv )
+{
+	drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_r128_context_regs_t *ctx = &sarea_priv->context_state;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 13 );
+
+	OUT_RING( CCE_PACKET0( R128_DST_PITCH_OFFSET_C, 11 ) );
+	OUT_RING( ctx->dst_pitch_offset_c );
+	OUT_RING( ctx->dp_gui_master_cntl_c );
+	OUT_RING( ctx->sc_top_left_c );
+	OUT_RING( ctx->sc_bottom_right_c );
+	OUT_RING( ctx->z_offset_c );
+	OUT_RING( ctx->z_pitch_c );
+	OUT_RING( ctx->z_sten_cntl_c );
+	OUT_RING( ctx->tex_cntl_c );
+	OUT_RING( ctx->misc_3d_state_cntl_reg );
+	OUT_RING( ctx->texture_clr_cmp_clr_c );
+	OUT_RING( ctx->texture_clr_cmp_msk_c );
+	OUT_RING( ctx->fog_color_c );
+
+	ADVANCE_RING();
+}
+
+static inline void r128_emit_setup( drm_r128_private_t *dev_priv )
+{
+	drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_r128_context_regs_t *ctx = &sarea_priv->context_state;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 3 );
+
+	OUT_RING( CCE_PACKET1( R128_SETUP_CNTL, R128_PM4_VC_FPU_SETUP ) );
+	OUT_RING( ctx->setup_cntl );
+	OUT_RING( ctx->pm4_vc_fpu_setup );
+
+	ADVANCE_RING();
+}
+
+static inline void r128_emit_masks( drm_r128_private_t *dev_priv )
+{
+	drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_r128_context_regs_t *ctx = &sarea_priv->context_state;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 5 );
+
+	OUT_RING( CCE_PACKET0( R128_DP_WRITE_MASK, 0 ) );
+	OUT_RING( ctx->dp_write_mask );
+
+	OUT_RING( CCE_PACKET0( R128_STEN_REF_MASK_C, 1 ) );
+	OUT_RING( ctx->sten_ref_mask_c );
+	OUT_RING( ctx->plane_3d_mask_c );
+
+	ADVANCE_RING();
+}
+
+static inline void r128_emit_window( drm_r128_private_t *dev_priv )
+{
+	drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_r128_context_regs_t *ctx = &sarea_priv->context_state;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 2 );
+
+	OUT_RING( CCE_PACKET0( R128_WINDOW_XY_OFFSET, 0 ) );
+	OUT_RING( ctx->window_xy_offset );
+
+	ADVANCE_RING();
+}
+
+static inline void r128_emit_tex0( drm_r128_private_t *dev_priv )
+{
+	drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_r128_context_regs_t *ctx = &sarea_priv->context_state;
+	drm_r128_texture_regs_t *tex = &sarea_priv->tex_state[0];
+	int i;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 7 + R128_TEX_MAXLEVELS );
+
+	OUT_RING( CCE_PACKET0( R128_PRIM_TEX_CNTL_C,
+			       2 + R128_TEX_MAXLEVELS ) );
+	OUT_RING( tex->tex_cntl );
+	OUT_RING( tex->tex_combine_cntl );
+	OUT_RING( ctx->tex_size_pitch_c );
+	for ( i = 0 ; i < R128_TEX_MAXLEVELS ; i++ ) {
+		OUT_RING( tex->tex_offset[i] );
+	}
+
+	OUT_RING( CCE_PACKET0( R128_CONSTANT_COLOR_C, 1 ) );
+	OUT_RING( ctx->constant_color_c );
+	OUT_RING( tex->tex_border_color );
+
+	ADVANCE_RING();
+}
+
+static inline void r128_emit_tex1( drm_r128_private_t *dev_priv )
+{
+	drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_r128_texture_regs_t *tex = &sarea_priv->tex_state[1];
+	int i;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 5 + R128_TEX_MAXLEVELS );
+
+	OUT_RING( CCE_PACKET0( R128_SEC_TEX_CNTL_C,
+			       1 + R128_TEX_MAXLEVELS ) );
+	OUT_RING( tex->tex_cntl );
+	OUT_RING( tex->tex_combine_cntl );
+	for ( i = 0 ; i < R128_TEX_MAXLEVELS ; i++ ) {
+		OUT_RING( tex->tex_offset[i] );
+	}
+
+	OUT_RING( CCE_PACKET0( R128_SEC_TEXTURE_BORDER_COLOR_C, 0 ) );
+	OUT_RING( tex->tex_border_color );
+
+	ADVANCE_RING();
+}
+
+static inline void r128_emit_state( drm_r128_private_t *dev_priv )
+{
+	drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int dirty = sarea_priv->dirty;
+
+	DRM_DEBUG( "%s: dirty=0x%08x\n", __FUNCTION__, dirty );
+
+	if ( dirty & R128_UPLOAD_CORE ) {
+		r128_emit_core( dev_priv );
+		sarea_priv->dirty &= ~R128_UPLOAD_CORE;
+	}
+
+	if ( dirty & R128_UPLOAD_CONTEXT ) {
+		r128_emit_context( dev_priv );
+		sarea_priv->dirty &= ~R128_UPLOAD_CONTEXT;
+	}
+
+	if ( dirty & R128_UPLOAD_SETUP ) {
+		r128_emit_setup( dev_priv );
+		sarea_priv->dirty &= ~R128_UPLOAD_SETUP;
+	}
+
+	if ( dirty & R128_UPLOAD_MASKS ) {
+		r128_emit_masks( dev_priv );
+		sarea_priv->dirty &= ~R128_UPLOAD_MASKS;
+	}
+
+	if ( dirty & R128_UPLOAD_WINDOW ) {
+		r128_emit_window( dev_priv );
+		sarea_priv->dirty &= ~R128_UPLOAD_WINDOW;
+	}
+
+	if ( dirty & R128_UPLOAD_TEX0 ) {
+		r128_emit_tex0( dev_priv );
+		sarea_priv->dirty &= ~R128_UPLOAD_TEX0;
+	}
+
+	if ( dirty & R128_UPLOAD_TEX1 ) {
+		r128_emit_tex1( dev_priv );
+		sarea_priv->dirty &= ~R128_UPLOAD_TEX1;
+	}
+
+	/* Turn off the texture cache flushing */
+	sarea_priv->context_state.tex_cntl_c &= ~R128_TEX_CACHE_FLUSH;
+
+	sarea_priv->dirty &= ~R128_REQUIRE_QUIESCENCE;
+}
+
+
+#if R128_PERFORMANCE_BOXES
+/* ================================================================
+ * Performance monitoring functions
+ */
+
+static void r128_clear_box( drm_r128_private_t *dev_priv,
+			    int x, int y, int w, int h,
+			    int r, int g, int b )
+{
+	u32 pitch, offset;
+	u32 fb_bpp, color;
+	RING_LOCALS;
+
+	switch ( dev_priv->fb_bpp ) {
+	case 16:
+		fb_bpp = R128_GMC_DST_16BPP;
+		color = (((r & 0xf8) << 8) |
+			 ((g & 0xfc) << 3) |
+			 ((b & 0xf8) >> 3));
+		break;
+	case 24:
+		fb_bpp = R128_GMC_DST_24BPP;
+		color = ((r << 16) | (g << 8) | b);
+		break;
+	case 32:
+		fb_bpp = R128_GMC_DST_32BPP;
+		color = (((0xff) << 24) | (r << 16) | (g <<  8) | b);
+		break;
+	default:
+		return;
+	}
+
+	offset = dev_priv->back_offset;
+	pitch = dev_priv->back_pitch >> 3;
+
+	BEGIN_RING( 6 );
+
+	OUT_RING( CCE_PACKET3( R128_CNTL_PAINT_MULTI, 4 ) );
+	OUT_RING( R128_GMC_DST_PITCH_OFFSET_CNTL
+		  | R128_GMC_BRUSH_SOLID_COLOR
+		  | fb_bpp
+		  | R128_GMC_SRC_DATATYPE_COLOR
+		  | R128_ROP3_P
+		  | R128_GMC_CLR_CMP_CNTL_DIS
+		  | R128_GMC_AUX_CLIP_DIS );
+
+	OUT_RING( (pitch << 21) | (offset >> 5) );
+	OUT_RING( color );
+
+	OUT_RING( (x << 16) | y );
+	OUT_RING( (w << 16) | h );
+
+	ADVANCE_RING();
+}
+
+static void r128_cce_performance_boxes( drm_r128_private_t *dev_priv )
+{
+	if ( atomic_read( &dev_priv->idle_count ) == 0 ) {
+		r128_clear_box( dev_priv, 64, 4, 8, 8, 0, 255, 0 );
+	} else {
+		atomic_set( &dev_priv->idle_count, 0 );
+	}
+}
+
+#endif
+
+
+/* ================================================================
+ * CCE command dispatch functions
+ */
+
+static void r128_print_dirty( const char *msg, unsigned int flags )
+{
+	DRM_INFO( "%s: (0x%x) %s%s%s%s%s%s%s%s%s\n",
+		  msg,
+		  flags,
+		  (flags & R128_UPLOAD_CORE)        ? "core, " : "",
+		  (flags & R128_UPLOAD_CONTEXT)     ? "context, " : "",
+		  (flags & R128_UPLOAD_SETUP)       ? "setup, " : "",
+		  (flags & R128_UPLOAD_TEX0)        ? "tex0, " : "",
+		  (flags & R128_UPLOAD_TEX1)        ? "tex1, " : "",
+		  (flags & R128_UPLOAD_MASKS)       ? "masks, " : "",
+		  (flags & R128_UPLOAD_WINDOW)      ? "window, " : "",
+		  (flags & R128_UPLOAD_CLIPRECTS)   ? "cliprects, " : "",
+		  (flags & R128_REQUIRE_QUIESCENCE) ? "quiescence, " : "" );
+}
+
+static void r128_cce_dispatch_clear( drm_device_t *dev,
+				     unsigned int flags,
+				     int cx, int cy, int cw, int ch,
+				     unsigned int clear_color,
+				     unsigned int clear_depth )
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	int nbox = sarea_priv->nbox;
+	drm_clip_rect_t *pbox = sarea_priv->boxes;
+	u32 fb_bpp, depth_bpp;
+	int i;
+	RING_LOCALS;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	r128_update_ring_snapshot( dev_priv );
+
+	switch ( dev_priv->fb_bpp ) {
+	case 16:
+		fb_bpp = R128_GMC_DST_16BPP;
+		break;
+	case 32:
+		fb_bpp = R128_GMC_DST_32BPP;
+		break;
+	default:
+		return;
+	}
+	switch ( dev_priv->depth_bpp ) {
+	case 16:
+		depth_bpp = R128_GMC_DST_16BPP;
+		break;
+	case 24:
+	case 32:
+		depth_bpp = R128_GMC_DST_32BPP;
+		break;
+	default:
+		return;
+	}
+
+	for ( i = 0 ; i < nbox ; i++ ) {
+		int x = pbox[i].x1;
+		int y = pbox[i].y1;
+		int w = pbox[i].x2 - x;
+		int h = pbox[i].y2 - y;
+
+		DRM_DEBUG( "dispatch clear %d,%d-%d,%d flags 0x%x\n",
+			   pbox[i].x1, pbox[i].y1, pbox[i].x2,
+			   pbox[i].y2, flags );
+
+		if ( flags & (R128_FRONT | R128_BACK) ) {
+			BEGIN_RING( 2 );
+
+			OUT_RING( CCE_PACKET0( R128_DP_WRITE_MASK, 0 ) );
+			OUT_RING( sarea_priv->context_state.plane_3d_mask_c );
+
+			ADVANCE_RING();
+		}
+
+		if ( flags & R128_FRONT ) {
+			BEGIN_RING( 6 );
+
+			OUT_RING( CCE_PACKET3( R128_CNTL_PAINT_MULTI, 4 ) );
+			OUT_RING( R128_GMC_DST_PITCH_OFFSET_CNTL
+				  | R128_GMC_BRUSH_SOLID_COLOR
+				  | fb_bpp
+				  | R128_GMC_SRC_DATATYPE_COLOR
+				  | R128_ROP3_P
+				  | R128_GMC_CLR_CMP_CNTL_DIS
+				  | R128_GMC_AUX_CLIP_DIS );
+
+			OUT_RING( dev_priv->front_pitch_offset_c );
+			OUT_RING( clear_color );
+
+			OUT_RING( (x << 16) | y );
+			OUT_RING( (w << 16) | h );
+
+			ADVANCE_RING();
+		}
+
+		if ( flags & R128_BACK ) {
+			BEGIN_RING( 6 );
+
+			OUT_RING( CCE_PACKET3( R128_CNTL_PAINT_MULTI, 4 ) );
+			OUT_RING( R128_GMC_DST_PITCH_OFFSET_CNTL
+				  | R128_GMC_BRUSH_SOLID_COLOR
+				  | fb_bpp
+				  | R128_GMC_SRC_DATATYPE_COLOR
+				  | R128_ROP3_P
+				  | R128_GMC_CLR_CMP_CNTL_DIS
+				  | R128_GMC_AUX_CLIP_DIS );
+
+			OUT_RING( dev_priv->back_pitch_offset_c );
+			OUT_RING( clear_color );
+
+			OUT_RING( (x << 16) | y );
+			OUT_RING( (w << 16) | h );
+
+			ADVANCE_RING();
+		}
+
+		if ( flags & R128_DEPTH ) {
+			BEGIN_RING( 6 );
+
+			OUT_RING( CCE_PACKET3( R128_CNTL_PAINT_MULTI, 4 ) );
+			OUT_RING( R128_GMC_DST_PITCH_OFFSET_CNTL
+				  | R128_GMC_BRUSH_SOLID_COLOR
+				  | depth_bpp
+				  | R128_GMC_SRC_DATATYPE_COLOR
+				  | R128_ROP3_P
+				  | R128_GMC_CLR_CMP_CNTL_DIS
+				  | R128_GMC_AUX_CLIP_DIS
+				  | R128_GMC_WR_MSK_DIS );
+
+			OUT_RING( dev_priv->depth_pitch_offset_c );
+			OUT_RING( clear_depth );
+
+			OUT_RING( (x << 16) | y );
+			OUT_RING( (w << 16) | h );
+
+			ADVANCE_RING();
+		}
+	}
+}
+
+static void r128_cce_dispatch_swap( drm_device_t *dev )
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	int nbox = sarea_priv->nbox;
+	drm_clip_rect_t *pbox = sarea_priv->boxes;
+	u32 fb_bpp;
+	int i;
+	RING_LOCALS;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	r128_update_ring_snapshot( dev_priv );
+
+#if R128_PERFORMANCE_BOXES
+	/* Do some trivial performance monitoring...
+	 */
+	r128_cce_performance_boxes( dev_priv );
+#endif
+
+	switch ( dev_priv->fb_bpp ) {
+	case 16:
+		fb_bpp = R128_GMC_DST_16BPP;
+		break;
+	case 32:
+	default:
+		fb_bpp = R128_GMC_DST_32BPP;
+		break;
+	}
+
+	for ( i = 0 ; i < nbox ; i++ ) {
+		int x = pbox[i].x1;
+		int y = pbox[i].y1;
+		int w = pbox[i].x2 - x;
+		int h = pbox[i].y2 - y;
+
+		BEGIN_RING( 7 );
+
+		OUT_RING( CCE_PACKET3( R128_CNTL_BITBLT_MULTI, 5 ) );
+		OUT_RING( R128_GMC_SRC_PITCH_OFFSET_CNTL
+			  | R128_GMC_DST_PITCH_OFFSET_CNTL
+			  | R128_GMC_BRUSH_NONE
+			  | fb_bpp
+			  | R128_GMC_SRC_DATATYPE_COLOR
+			  | R128_ROP3_S
+			  | R128_DP_SRC_SOURCE_MEMORY
+			  | R128_GMC_CLR_CMP_CNTL_DIS
+			  | R128_GMC_AUX_CLIP_DIS
+			  | R128_GMC_WR_MSK_DIS );
+
+		OUT_RING( dev_priv->back_pitch_offset_c );
+		OUT_RING( dev_priv->front_pitch_offset_c );
+
+		OUT_RING( (x << 16) | y );
+		OUT_RING( (x << 16) | y );
+		OUT_RING( (w << 16) | h );
+
+		ADVANCE_RING();
+	}
+
+	/* Increment the frame counter.  The client-side 3D driver must
+	 * throttle the framerate by waiting for this value before
+	 * performing the swapbuffer ioctl.
+	 */
+	dev_priv->sarea_priv->last_frame++;
+
+	BEGIN_RING( 2 );
+
+	OUT_RING( CCE_PACKET0( R128_LAST_FRAME_REG, 0 ) );
+	OUT_RING( dev_priv->sarea_priv->last_frame );
+
+	ADVANCE_RING();
+}
+
+static void r128_cce_dispatch_vertex( drm_device_t *dev,
+				      drm_buf_t *buf )
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	drm_r128_buf_priv_t *buf_priv = buf->dev_private;
+	drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	int format = sarea_priv->vc_format;
+	int offset = dev_priv->buffers->offset + buf->offset - dev->agp->base;
+	int size = buf->used;
+	int prim = buf_priv->prim;
+	int i = 0;
+	RING_LOCALS;
+	DRM_DEBUG( "%s: buf=%d nbox=%d\n",
+		   __FUNCTION__, buf->idx, sarea_priv->nbox );
+
+	r128_update_ring_snapshot( dev_priv );
+
+	if ( 0 )
+		r128_print_dirty( "dispatch_vertex", sarea_priv->dirty );
+
+	if ( buf->used ) {
+		buf_priv->dispatched = 1;
+
+		if ( sarea_priv->dirty & ~R128_UPLOAD_CLIPRECTS ) {
+			r128_emit_state( dev_priv );
+		}
+
+		do {
+			/* Emit the next set of up to three cliprects */
+			if ( i < sarea_priv->nbox ) {
+				r128_emit_clip_rects( dev_priv,
+						      &sarea_priv->boxes[i],
+						      sarea_priv->nbox - i );
+			}
+
+			/* Emit the vertex buffer rendering commands */
+			BEGIN_RING( 5 );
+
+			OUT_RING( CCE_PACKET3( R128_3D_RNDR_GEN_INDX_PRIM, 3 ) );
+			OUT_RING( offset );
+			OUT_RING( size );
+			OUT_RING( format );
+			OUT_RING( prim | R128_CCE_VC_CNTL_PRIM_WALK_LIST |
+				  (size << R128_CCE_VC_CNTL_NUM_SHIFT) );
+
+			ADVANCE_RING();
+
+			i += 3;
+		} while ( i < sarea_priv->nbox );
+	}
+
+	if ( buf_priv->discard ) {
+		buf_priv->age = dev_priv->sarea_priv->last_dispatch;
+
+		/* Emit the vertex buffer age */
+		BEGIN_RING( 2 );
+
+		OUT_RING( CCE_PACKET0( R128_LAST_DISPATCH_REG, 0 ) );
+		OUT_RING( buf_priv->age );
+
+		ADVANCE_RING();
+
+		buf->pending = 1;
+		buf->used = 0;
+		/* FIXME: Check dispatched field */
+		buf_priv->dispatched = 0;
+	}
+
+	dev_priv->sarea_priv->last_dispatch++;
+
+#if 0
+	if ( dev_priv->submit_age == R128_MAX_VB_AGE ) {
+		ret = r128_do_cce_idle( dev_priv );
+		if ( ret < 0 ) return ret;
+		dev_priv->submit_age = 0;
+		r128_freelist_reset( dev );
+	}
+#endif
+
+	sarea_priv->dirty &= ~R128_UPLOAD_CLIPRECTS;
+	sarea_priv->nbox = 0;
+}
+
+
+
+
+static void r128_cce_dispatch_indirect( drm_device_t *dev,
+					drm_buf_t *buf,
+					int start, int end )
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	drm_r128_buf_priv_t *buf_priv = buf->dev_private;
+	RING_LOCALS;
+	DRM_DEBUG( "indirect: buf=%d s=0x%x e=0x%x\n",
+		   buf->idx, start, end );
+
+	r128_update_ring_snapshot( dev_priv );
+
+	if ( start != end ) {
+		int offset = (dev_priv->buffers->offset - dev->agp->base
+			      + buf->offset + start);
+		int dwords = (end - start + 3) / sizeof(u32);
+
+		/* Indirect buffer data must be an even number of
+		 * dwords, so if we've been given an odd number we must
+		 * pad the data with a Type-2 CCE packet.
+		 */
+		if ( dwords & 1 ) {
+			u32 *data = (u32 *)
+				((char *)dev_priv->buffers->handle
+				 + buf->offset + start);
+			data[dwords++] = R128_CCE_PACKET2;
+		}
+
+		buf_priv->dispatched = 1;
+
+		/* Fire off the indirect buffer */
+		BEGIN_RING( 3 );
+
+		OUT_RING( CCE_PACKET0( R128_PM4_IW_INDOFF, 1 ) );
+		OUT_RING( offset );
+		OUT_RING( dwords );
+
+		ADVANCE_RING();
+	}
+
+	if ( buf_priv->discard ) {
+		buf_priv->age = dev_priv->sarea_priv->last_dispatch;
+
+		/* Emit the indirect buffer age */
+		BEGIN_RING( 2 );
+
+		OUT_RING( CCE_PACKET0( R128_LAST_DISPATCH_REG, 0 ) );
+		OUT_RING( buf_priv->age );
+
+		ADVANCE_RING();
+
+		buf->pending = 1;
+		buf->used = 0;
+		/* FIXME: Check dispatched field */
+		buf_priv->dispatched = 0;
+	}
+
+	dev_priv->sarea_priv->last_dispatch++;
+
+#if 0
+	if ( dev_priv->submit_age == R128_MAX_VB_AGE ) {
+		ret = r128_do_cce_idle( dev_priv );
+		if ( ret < 0 ) return ret;
+		dev_priv->submit_age = 0;
+		r128_freelist_reset( dev );
+	}
+#endif
+}
+
+static void r128_cce_dispatch_indices( drm_device_t *dev,
+				       drm_buf_t *buf,
+				       int start, int end,
+				       int count )
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	drm_r128_buf_priv_t *buf_priv = buf->dev_private;
+	drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	int format = sarea_priv->vc_format;
+	int offset = dev_priv->buffers->offset - dev->agp->base;
+	int prim = buf_priv->prim;
+	u32 *data;
+	int dwords;
+	int i = 0;
+	RING_LOCALS;
+	DRM_DEBUG( "indices: s=%d e=%d c=%d\n", start, end, count );
+
+	r128_update_ring_snapshot( dev_priv );
+
+	if ( 0 )
+		r128_print_dirty( "dispatch_indices", sarea_priv->dirty );
+
+	if ( start != end ) {
+		buf_priv->dispatched = 1;
+
+		if ( sarea_priv->dirty & ~R128_UPLOAD_CLIPRECTS ) {
+			r128_emit_state( dev_priv );
+		}
+
+		dwords = (end - start + 3) / sizeof(u32);
+
+		data = (u32 *)((char *)dev_priv->buffers->handle
+			       + buf->offset + start);
+
+		data[0] = CCE_PACKET3( R128_3D_RNDR_GEN_INDX_PRIM, dwords-2 );
+
+		data[1] = offset;
+		data[2] = R128_MAX_VB_VERTS;
+		data[3] = format;
+		data[4] = (prim | R128_CCE_VC_CNTL_PRIM_WALK_IND |
+			   (count << 16));
+
+		if ( count & 0x1 ) {
+			data[dwords-1] &= 0x0000ffff;
+		}
+
+		do {
+			/* Emit the next set of up to three cliprects */
+			if ( i < sarea_priv->nbox ) {
+				r128_emit_clip_rects( dev_priv,
+						      &sarea_priv->boxes[i],
+						      sarea_priv->nbox - i );
+			}
+
+			r128_cce_dispatch_indirect( dev, buf, start, end );
+
+			i += 3;
+		} while ( i < sarea_priv->nbox );
+	}
+
+	if ( buf_priv->discard ) {
+		buf_priv->age = dev_priv->sarea_priv->last_dispatch;
+
+		/* Emit the vertex buffer age */
+		BEGIN_RING( 2 );
+
+		OUT_RING( CCE_PACKET0( R128_LAST_DISPATCH_REG, 0 ) );
+		OUT_RING( buf_priv->age );
+
+		ADVANCE_RING();
+
+		buf->pending = 1;
+		/* FIXME: Check dispatched field */
+		buf_priv->dispatched = 0;
+	}
+
+	dev_priv->sarea_priv->last_dispatch++;
+
+#if 0
+	if ( dev_priv->submit_age == R128_MAX_VB_AGE ) {
+		ret = r128_do_cce_idle( dev_priv );
+		if ( ret < 0 ) return ret;
+		dev_priv->submit_age = 0;
+		r128_freelist_reset( dev );
+	}
+#endif
+
+	sarea_priv->dirty &= ~R128_UPLOAD_CLIPRECTS;
+	sarea_priv->nbox = 0;
+}
+
+static int r128_cce_dispatch_blit( drm_device_t *dev,
+				   drm_r128_blit_t *blit )
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	drm_device_dma_t *dma = dev->dma;
+	drm_buf_t *buf;
+	drm_r128_buf_priv_t *buf_priv;
+	u32 *data;
+	int dword_shift, dwords;
+	RING_LOCALS;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	r128_update_ring_snapshot( dev_priv );
+
+	/* The compiler won't optimize away a division by a variable,
+	 * even if the only legal values are powers of two.  Thus, we'll
+	 * use a shift instead.
+	 */
+	switch ( blit->format ) {
+	case R128_DATATYPE_ARGB1555:
+	case R128_DATATYPE_RGB565:
+	case R128_DATATYPE_ARGB4444:
+		dword_shift = 1;
+		break;
+	case R128_DATATYPE_ARGB8888:
+		dword_shift = 0;
+		break;
+	default:
+		DRM_ERROR( "invalid blit format %d\n", blit->format );
+		return -EINVAL;
+	}
+
+	/* Flush the pixel cache, and mark the contents as Read Invalid.
+	 * This ensures no pixel data gets mixed up with the texture
+	 * data from the host data blit, otherwise part of the texture
+	 * image may be corrupted.
+	 */
+	BEGIN_RING( 2 );
+
+	OUT_RING( CCE_PACKET0( R128_PC_GUI_CTLSTAT, 0 ) );
+	OUT_RING( R128_PC_RI_GUI | R128_PC_FLUSH_GUI );
+
+	ADVANCE_RING();
+
+	/* Dispatch the indirect buffer.
+	 */
+	buf = dma->buflist[blit->idx];
+	buf_priv = buf->dev_private;
+
+	if ( buf->pid != current->pid ) {
+		DRM_ERROR( "process %d using buffer owned by %d\n",
+			   current->pid, buf->pid );
+		return -EINVAL;
+	}
+	if ( buf->pending ) {
+		DRM_ERROR( "sending pending buffer %d\n", blit->idx );
+		return -EINVAL;
+	}
+
+	buf_priv->discard = 1;
+
+	dwords = (blit->width * blit->height) >> dword_shift;
+
+	data = (u32 *)((char *)dev_priv->buffers->handle + buf->offset);
+
+	data[0] = CCE_PACKET3( R128_CNTL_HOSTDATA_BLT, dwords + 6 );
+	data[1] = ( R128_GMC_DST_PITCH_OFFSET_CNTL
+		    | R128_GMC_BRUSH_NONE
+		    | (blit->format << 8)
+		    | R128_GMC_SRC_DATATYPE_COLOR
+		    | R128_ROP3_S
+		    | R128_DP_SRC_SOURCE_HOST_DATA
+		    | R128_GMC_CLR_CMP_CNTL_DIS
+		    | R128_GMC_AUX_CLIP_DIS
+		    | R128_GMC_WR_MSK_DIS );
+
+	data[2] = (blit->pitch << 21) | (blit->offset >> 5);
+	data[3] = 0xffffffff;
+	data[4] = 0xffffffff;
+	data[5] = (blit->y << 16) | blit->x;
+	data[6] = (blit->height << 16) | blit->width;
+	data[7] = dwords;
+
+	buf->used = (dwords + 8) * sizeof(u32);
+
+	r128_cce_dispatch_indirect( dev, buf, 0, buf->used );
+
+	/* Flush the pixel cache after the blit completes.  This ensures
+	 * the texture data is written out to memory before rendering
+	 * continues.
+	 */
+	BEGIN_RING( 2 );
+
+	OUT_RING( CCE_PACKET0( R128_PC_GUI_CTLSTAT, 0 ) );
+	OUT_RING( R128_PC_FLUSH_GUI );
+
+	ADVANCE_RING();
+
+	return 0;
+}
+
+
+/* ================================================================
+ * Tiled depth buffer management
+ *
+ * FIXME: These should all set the destination write mask for when we
+ * have hardware stencil support.
+ */
+
+static int r128_cce_dispatch_write_span( drm_device_t *dev,
+					 drm_r128_depth_t *depth )
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	int count, x, y;
+	u32 *buffer;
+	u8 *mask;
+	u32 depth_bpp;
+	int i;
+	RING_LOCALS;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	r128_update_ring_snapshot( dev_priv );
+
+	switch ( dev_priv->depth_bpp ) {
+	case 16:
+		depth_bpp = R128_GMC_DST_16BPP;
+		break;
+	case 24:
+	case 32:
+		depth_bpp = R128_GMC_DST_32BPP;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	count = depth->n;
+	if ( copy_from_user( &x, depth->x, sizeof(x) ) ) {
+		return -EFAULT;
+	}
+	if ( copy_from_user( &y, depth->y, sizeof(y) ) ) {
+		return -EFAULT;
+	}
+
+	buffer = kmalloc( depth->n * sizeof(u32), 0 );
+	if ( buffer == NULL )
+		return -ENOMEM;
+	if ( copy_from_user( buffer, depth->buffer,
+			     depth->n * sizeof(u32) ) ) {
+		kfree( buffer );
+		return -EFAULT;
+	}
+
+	if ( depth->mask ) {
+		mask = kmalloc( depth->n * sizeof(u8), 0 );
+		if ( mask == NULL ) {
+			kfree( buffer );
+			return -ENOMEM;
+		}
+		if ( copy_from_user( mask, depth->mask,
+				     depth->n * sizeof(u8) ) ) {
+			kfree( buffer );
+			kfree( mask );
+			return -EFAULT;
+		}
+
+		for ( i = 0 ; i < count ; i++, x++ ) {
+			if ( mask[i] ) {
+				BEGIN_RING( 6 );
+
+				OUT_RING( CCE_PACKET3( R128_CNTL_PAINT_MULTI,
+						       4 ) );
+				OUT_RING( R128_GMC_DST_PITCH_OFFSET_CNTL
+					  | R128_GMC_BRUSH_SOLID_COLOR
+					  | depth_bpp
+					  | R128_GMC_SRC_DATATYPE_COLOR
+					  | R128_ROP3_P
+					  | R128_GMC_CLR_CMP_CNTL_DIS
+					  | R128_GMC_WR_MSK_DIS );
+
+				OUT_RING( dev_priv->depth_pitch_offset_c );
+				OUT_RING( buffer[i] );
+
+				OUT_RING( (x << 16) | y );
+				OUT_RING( (1 << 16) | 1 );
+
+				ADVANCE_RING();
+			}
+		}
+
+		kfree( mask );
+	} else {
+		for ( i = 0 ; i < count ; i++, x++ ) {
+			BEGIN_RING( 6 );
+
+			OUT_RING( CCE_PACKET3( R128_CNTL_PAINT_MULTI, 4 ) );
+			OUT_RING( R128_GMC_DST_PITCH_OFFSET_CNTL
+				  | R128_GMC_BRUSH_SOLID_COLOR
+				  | depth_bpp
+				  | R128_GMC_SRC_DATATYPE_COLOR
+				  | R128_ROP3_P
+				  | R128_GMC_CLR_CMP_CNTL_DIS
+				  | R128_GMC_WR_MSK_DIS );
+
+			OUT_RING( dev_priv->depth_pitch_offset_c );
+			OUT_RING( buffer[i] );
+
+			OUT_RING( (x << 16) | y );
+			OUT_RING( (1 << 16) | 1 );
+
+			ADVANCE_RING();
+		}
+	}
+
+	kfree( buffer );
+
+	return 0;
+}
+
+static int r128_cce_dispatch_write_pixels( drm_device_t *dev,
+					   drm_r128_depth_t *depth )
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	int count, *x, *y;
+	u32 *buffer;
+	u8 *mask;
+	u32 depth_bpp;
+	int i;
+	RING_LOCALS;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	r128_update_ring_snapshot( dev_priv );
+
+	switch ( dev_priv->depth_bpp ) {
+	case 16:
+		depth_bpp = R128_GMC_DST_16BPP;
+		break;
+	case 24:
+	case 32:
+		depth_bpp = R128_GMC_DST_32BPP;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	count = depth->n;
+
+	x = kmalloc( count * sizeof(*x), 0 );
+	if ( x == NULL ) {
+		return -ENOMEM;
+	}
+	y = kmalloc( count * sizeof(*y), 0 );
+	if ( y == NULL ) {
+		kfree( x );
+		return -ENOMEM;
+	}
+	if ( copy_from_user( x, depth->x, count * sizeof(int) ) ) {
+		kfree( x );
+		kfree( y );
+		return -EFAULT;
+	}
+	if ( copy_from_user( y, depth->y, count * sizeof(int) ) ) {
+		kfree( x );
+		kfree( y );
+		return -EFAULT;
+	}
+
+	buffer = kmalloc( depth->n * sizeof(u32), 0 );
+	if ( buffer == NULL ) {
+		kfree( x );
+		kfree( y );
+		return -ENOMEM;
+	}
+	if ( copy_from_user( buffer, depth->buffer,
+			     depth->n * sizeof(u32) ) ) {
+		kfree( x );
+		kfree( y );
+		kfree( buffer );
+		return -EFAULT;
+	}
+
+	if ( depth->mask ) {
+		mask = kmalloc( depth->n * sizeof(u8), 0 );
+		if ( mask == NULL ) {
+			kfree( x );
+			kfree( y );
+			kfree( buffer );
+			return -ENOMEM;
+		}
+		if ( copy_from_user( mask, depth->mask,
+				     depth->n * sizeof(u8) ) ) {
+			kfree( x );
+			kfree( y );
+			kfree( buffer );
+			kfree( mask );
+			return -EFAULT;
+		}
+
+		for ( i = 0 ; i < count ; i++ ) {
+			if ( mask[i] ) {
+				BEGIN_RING( 6 );
+
+				OUT_RING( CCE_PACKET3( R128_CNTL_PAINT_MULTI,
+						       4 ) );
+				OUT_RING( R128_GMC_DST_PITCH_OFFSET_CNTL
+					  | R128_GMC_BRUSH_SOLID_COLOR
+					  | depth_bpp
+					  | R128_GMC_SRC_DATATYPE_COLOR
+					  | R128_ROP3_P
+					  | R128_GMC_CLR_CMP_CNTL_DIS
+					  | R128_GMC_WR_MSK_DIS );
+
+				OUT_RING( dev_priv->depth_pitch_offset_c );
+				OUT_RING( buffer[i] );
+
+				OUT_RING( (x[i] << 16) | y[i] );
+				OUT_RING( (1 << 16) | 1 );
+
+				ADVANCE_RING();
+			}
+		}
+
+		kfree( mask );
+	} else {
+		for ( i = 0 ; i < count ; i++ ) {
+			BEGIN_RING( 6 );
+
+			OUT_RING( CCE_PACKET3( R128_CNTL_PAINT_MULTI, 4 ) );
+			OUT_RING( R128_GMC_DST_PITCH_OFFSET_CNTL
+				  | R128_GMC_BRUSH_SOLID_COLOR
+				  | depth_bpp
+				  | R128_GMC_SRC_DATATYPE_COLOR
+				  | R128_ROP3_P
+				  | R128_GMC_CLR_CMP_CNTL_DIS
+				  | R128_GMC_WR_MSK_DIS );
+
+			OUT_RING( dev_priv->depth_pitch_offset_c );
+			OUT_RING( buffer[i] );
+
+			OUT_RING( (x[i] << 16) | y[i] );
+			OUT_RING( (1 << 16) | 1 );
+
+			ADVANCE_RING();
+		}
+	}
+
+	kfree( x );
+	kfree( y );
+	kfree( buffer );
+
+	return 0;
+}
+
+static int r128_cce_dispatch_read_span( drm_device_t *dev,
+					drm_r128_depth_t *depth )
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	int count, x, y;
+	u32 depth_bpp;
+	RING_LOCALS;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	r128_update_ring_snapshot( dev_priv );
+
+	switch ( dev_priv->depth_bpp ) {
+	case 16:
+		depth_bpp = R128_GMC_DST_16BPP;
+		break;
+	case 24:
+	case 32:
+		depth_bpp = R128_GMC_DST_32BPP;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	count = depth->n;
+	if ( copy_from_user( &x, depth->x, sizeof(x) ) ) {
+		return -EFAULT;
+	}
+	if ( copy_from_user( &y, depth->y, sizeof(y) ) ) {
+		return -EFAULT;
+	}
+
+	BEGIN_RING( 7 );
+
+	OUT_RING( CCE_PACKET3( R128_CNTL_BITBLT_MULTI, 5 ) );
+	OUT_RING( R128_GMC_SRC_PITCH_OFFSET_CNTL
+		  | R128_GMC_DST_PITCH_OFFSET_CNTL
+		  | R128_GMC_BRUSH_NONE
+		  | depth_bpp
+		  | R128_GMC_SRC_DATATYPE_COLOR
+		  | R128_ROP3_S
+		  | R128_DP_SRC_SOURCE_MEMORY
+		  | R128_GMC_CLR_CMP_CNTL_DIS
+		  | R128_GMC_WR_MSK_DIS );
+
+	OUT_RING( dev_priv->depth_pitch_offset_c );
+	OUT_RING( dev_priv->span_pitch_offset_c );
+
+	OUT_RING( (x << 16) | y );
+	OUT_RING( (0 << 16) | 0 );
+	OUT_RING( (count << 16) | 1 );
+
+	ADVANCE_RING();
+
+	return 0;
+}
+
+static int r128_cce_dispatch_read_pixels( drm_device_t *dev,
+					  drm_r128_depth_t *depth )
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	int count, *x, *y;
+	u32 depth_bpp;
+	int i;
+	RING_LOCALS;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	r128_update_ring_snapshot( dev_priv );
+
+	switch ( dev_priv->depth_bpp ) {
+	case 16:
+		depth_bpp = R128_GMC_DST_16BPP;
+		break;
+	case 24:
+	case 32:
+		depth_bpp = R128_GMC_DST_32BPP;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	count = depth->n;
+	if ( count > dev_priv->depth_pitch ) {
+		count = dev_priv->depth_pitch;
+	}
+
+	x = kmalloc( count * sizeof(*x), 0 );
+	if ( x == NULL ) {
+		return -ENOMEM;
+	}
+	y = kmalloc( count * sizeof(*y), 0 );
+	if ( y == NULL ) {
+		kfree( x );
+		return -ENOMEM;
+	}
+	if ( copy_from_user( x, depth->x, count * sizeof(int) ) ) {
+		kfree( x );
+		kfree( y );
+		return -EFAULT;
+	}
+	if ( copy_from_user( y, depth->y, count * sizeof(int) ) ) {
+		kfree( x );
+		kfree( y );
+		return -EFAULT;
+	}
+
+	for ( i = 0 ; i < count ; i++ ) {
+		BEGIN_RING( 7 );
+
+		OUT_RING( CCE_PACKET3( R128_CNTL_BITBLT_MULTI, 5 ) );
+		OUT_RING( R128_GMC_SRC_PITCH_OFFSET_CNTL
+			  | R128_GMC_DST_PITCH_OFFSET_CNTL
+			  | R128_GMC_BRUSH_NONE
+			  | depth_bpp
+			  | R128_GMC_SRC_DATATYPE_COLOR
+			  | R128_ROP3_S
+			  | R128_DP_SRC_SOURCE_MEMORY
+			  | R128_GMC_CLR_CMP_CNTL_DIS
+			  | R128_GMC_WR_MSK_DIS );
+
+		OUT_RING( dev_priv->depth_pitch_offset_c );
+		OUT_RING( dev_priv->span_pitch_offset_c );
+
+		OUT_RING( (x[i] << 16) | y[i] );
+		OUT_RING( (i << 16) | 0 );
+		OUT_RING( (1 << 16) | 1 );
+
+		ADVANCE_RING();
+	}
+
+	kfree( x );
+	kfree( y );
+
+	return 0;
+}
+
+
+/* ================================================================
+ * Polygon stipple
+ */
+
+static void r128_cce_dispatch_stipple( drm_device_t *dev, u32 *stipple )
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	int i;
+	RING_LOCALS;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	r128_update_ring_snapshot( dev_priv );
+
+	BEGIN_RING( 33 );
+
+	OUT_RING(  CCE_PACKET0( R128_BRUSH_DATA0, 31 ) );
+	for ( i = 0 ; i < 32 ; i++ ) {
+		OUT_RING( stipple[i] );
+	}
+
+	ADVANCE_RING();
+}
+
+
+/* ================================================================
+ * IOCTL functions
+ */
+
+int r128_cce_clear( struct inode *inode, struct file *filp,
+		    unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_r128_clear_t clear;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "r128_cce_clear called without lock held\n" );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &clear, (drm_r128_clear_t *) arg,
+			     sizeof(clear) ) )
+		return -EFAULT;
+
+	if ( sarea_priv->nbox > R128_NR_SAREA_CLIPRECTS )
+		sarea_priv->nbox = R128_NR_SAREA_CLIPRECTS;
+
+	r128_cce_dispatch_clear( dev, clear.flags,
+				 clear.x, clear.y, clear.w, clear.h,
+				 clear.clear_color, clear.clear_depth );
+
+	/* Make sure we restore the 3D state next time.
+	 */
+	dev_priv->sarea_priv->dirty |= R128_UPLOAD_CONTEXT | R128_UPLOAD_MASKS;
+
+	return 0;
+}
+
+int r128_cce_swap( struct inode *inode, struct file *filp,
+		   unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "r128_cce_swap called without lock held\n" );
+		return -EINVAL;
+	}
+
+	if ( sarea_priv->nbox > R128_NR_SAREA_CLIPRECTS )
+		sarea_priv->nbox = R128_NR_SAREA_CLIPRECTS;
+
+	r128_cce_dispatch_swap( dev );
+
+	/* Make sure we restore the 3D state next time.
+	 */
+	dev_priv->sarea_priv->dirty |= R128_UPLOAD_CONTEXT | R128_UPLOAD_MASKS;
+
+	return 0;
+}
+
+int r128_cce_vertex( struct inode *inode, struct file *filp,
+		     unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	drm_device_dma_t *dma = dev->dma;
+	drm_buf_t *buf;
+	drm_r128_buf_priv_t *buf_priv;
+	drm_r128_vertex_t vertex;
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+	if ( !dev_priv || dev_priv->is_pci ) {
+		DRM_ERROR( "%s called with a PCI card\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &vertex, (drm_r128_vertex_t *)arg,
+			     sizeof(vertex) ) )
+		return -EFAULT;
+
+	DRM_DEBUG( "%s: pid=%d index=%d count=%d discard=%d\n",
+		   __FUNCTION__, current->pid,
+		   vertex.idx, vertex.count, vertex.discard );
+
+	if ( vertex.idx < 0 || vertex.idx >= dma->buf_count ) {
+		DRM_ERROR( "buffer index %d (of %d max)\n",
+			   vertex.idx, dma->buf_count - 1 );
+		return -EINVAL;
+	}
+	if ( vertex.prim < 0 ||
+	     vertex.prim > R128_CCE_VC_CNTL_PRIM_TYPE_TRI_TYPE2 ) {
+		DRM_ERROR( "buffer prim %d\n", vertex.prim );
+		return -EINVAL;
+	}
+
+	buf = dma->buflist[vertex.idx];
+	buf_priv = buf->dev_private;
+
+	if ( buf->pid != current->pid ) {
+		DRM_ERROR( "process %d using buffer owned by %d\n",
+			   current->pid, buf->pid );
+		return -EINVAL;
+	}
+	if ( buf->pending ) {
+		DRM_ERROR( "sending pending buffer %d\n", vertex.idx );
+		return -EINVAL;
+	}
+
+	buf->used = vertex.count;
+	buf_priv->prim = vertex.prim;
+	buf_priv->discard = vertex.discard;
+
+	r128_cce_dispatch_vertex( dev, buf );
+
+	return 0;
+}
+
+int r128_cce_indices( struct inode *inode, struct file *filp,
+		      unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	drm_device_dma_t *dma = dev->dma;
+	drm_buf_t *buf;
+	drm_r128_buf_priv_t *buf_priv;
+	drm_r128_indices_t elts;
+	int count;
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+	if ( !dev_priv || dev_priv->is_pci ) {
+		DRM_ERROR( "%s called with a PCI card\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &elts, (drm_r128_indices_t *)arg,
+			     sizeof(elts) ) )
+		return -EFAULT;
+
+	DRM_DEBUG( "%s: pid=%d buf=%d s=%d e=%d d=%d\n",
+		   __FUNCTION__, current->pid,
+		   elts.idx, elts.start, elts.end, elts.discard );
+
+	if ( elts.idx < 0 || elts.idx >= dma->buf_count ) {
+		DRM_ERROR( "buffer index %d (of %d max)\n",
+			   elts.idx, dma->buf_count - 1 );
+		return -EINVAL;
+	}
+	if ( elts.prim < 0 ||
+	     elts.prim > R128_CCE_VC_CNTL_PRIM_TYPE_TRI_TYPE2 ) {
+		DRM_ERROR( "buffer prim %d\n", elts.prim );
+		return -EINVAL;
+	}
+
+	buf = dma->buflist[elts.idx];
+	buf_priv = buf->dev_private;
+
+	if ( buf->pid != current->pid ) {
+		DRM_ERROR( "process %d using buffer owned by %d\n",
+			   current->pid, buf->pid );
+		return -EINVAL;
+	}
+	if ( buf->pending ) {
+		DRM_ERROR( "sending pending buffer %d\n", elts.idx );
+		return -EINVAL;
+	}
+
+	count = (elts.end - elts.start) / sizeof(u16);
+	elts.start -= R128_INDEX_PRIM_OFFSET;
+
+	if ( elts.start & 0x7 ) {
+		DRM_ERROR( "misaligned buffer 0x%x\n", elts.start );
+		return -EINVAL;
+	}
+	if ( elts.start < buf->used ) {
+		DRM_ERROR( "no header 0x%x - 0x%x\n", elts.start, buf->used );
+		return -EINVAL;
+	}
+
+	buf->used = elts.end;
+	buf_priv->prim = elts.prim;
+	buf_priv->discard = elts.discard;
+
+	r128_cce_dispatch_indices( dev, buf, elts.start, elts.end, count );
+
+	return 0;
+}
+
+int r128_cce_blit( struct inode *inode, struct file *filp,
+		   unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_device_dma_t *dma = dev->dma;
+	drm_r128_blit_t blit;
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &blit, (drm_r128_blit_t *)arg,
+			     sizeof(blit) ) )
+		return -EFAULT;
+
+	DRM_DEBUG( "%s: pid=%d index=%d\n",
+		   __FUNCTION__, current->pid, blit.idx );
+
+	if ( blit.idx < 0 || blit.idx >= dma->buf_count ) {
+		DRM_ERROR( "buffer index %d (of %d max)\n",
+			   blit.idx, dma->buf_count - 1 );
+		return -EINVAL;
+	}
+
+	return r128_cce_dispatch_blit( dev, &blit );
+}
+
+int r128_cce_depth( struct inode *inode, struct file *filp,
+		    unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_r128_depth_t depth;
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &depth, (drm_r128_depth_t *)arg,
+			     sizeof(depth) ) )
+		return -EFAULT;
+
+	switch ( depth.func ) {
+	case R128_WRITE_SPAN:
+		return r128_cce_dispatch_write_span( dev, &depth );
+	case R128_WRITE_PIXELS:
+		return r128_cce_dispatch_write_pixels( dev, &depth );
+	case R128_READ_SPAN:
+		return r128_cce_dispatch_read_span( dev, &depth );
+	case R128_READ_PIXELS:
+		return r128_cce_dispatch_read_pixels( dev, &depth );
+	}
+
+	return -EINVAL;
+}
+
+int r128_cce_stipple( struct inode *inode, struct file *filp,
+		      unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_r128_stipple_t stipple;
+	u32 mask[32];
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &stipple, (drm_r128_stipple_t *)arg,
+			     sizeof(stipple) ) )
+		return -EFAULT;
+
+	if ( copy_from_user( &mask, stipple.mask,
+			     32 * sizeof(u32) ) )
+		return -EFAULT;
+
+	r128_cce_dispatch_stipple( dev, mask );
+
+	return 0;
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/radeon_bufs.c linux/drivers/char/drm-4.0/radeon_bufs.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/radeon_bufs.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/radeon_bufs.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,312 @@
+/* radeon_bufs.c -- IOCTLs to manage buffers -*- linux-c -*-
+ *
+ * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Fremont, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Kevin E. Martin <martin@valinux.com>
+ *          Rickard E. (Rik) Faith <faith@valinux.com>
+ *	    Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include "drmP.h"
+#include "radeon_drv.h"
+#include "linux/un.h"
+
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+int radeon_addbufs_agp(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg)
+{
+	drm_file_t       *priv = filp->private_data;
+	drm_device_t     *dev  = priv->dev;
+	drm_device_dma_t *dma  = dev->dma;
+	drm_buf_desc_t    request;
+	drm_buf_entry_t  *entry;
+	drm_buf_t        *buf;
+	unsigned long     offset;
+	unsigned long     agp_offset;
+	int               count;
+	int               order;
+	int               size;
+	int               alignment;
+	int               page_order;
+	int               total;
+	int               byte_count;
+	int               i;
+
+	if (!dma) return -EINVAL;
+
+	if (copy_from_user(&request, (drm_buf_desc_t *)arg, sizeof(request)))
+		return -EFAULT;
+
+	count      = request.count;
+	order      = drm_order(request.size);
+	size       = 1 << order;
+
+	alignment  = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size):size;
+	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
+	total      = PAGE_SIZE << page_order;
+
+	byte_count = 0;
+	agp_offset = dev->agp->base + request.agp_start;
+
+	DRM_DEBUG("count:      %d\n",  count);
+	DRM_DEBUG("order:      %d\n",  order);
+	DRM_DEBUG("size:       %d\n",  size);
+	DRM_DEBUG("agp_offset: %ld\n", agp_offset);
+	DRM_DEBUG("alignment:  %d\n",  alignment);
+	DRM_DEBUG("page_order: %d\n",  page_order);
+	DRM_DEBUG("total:      %d\n",  total);
+
+	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+	if (dev->queue_count) return -EBUSY; /* Not while in use */
+
+	spin_lock(&dev->count_lock);
+	if (dev->buf_use) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	atomic_inc(&dev->buf_alloc);
+	spin_unlock(&dev->count_lock);
+
+	down(&dev->struct_sem);
+	entry = &dma->bufs[order];
+	if (entry->buf_count) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM; /* May only call once for each order */
+	}
+
+	/* Might be too low a limit. XFree folks need to fix this properly */
+	
+	if(count < 0 || count > 4096)
+	{
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -EINVAL;
+	}
+		
+	entry->buflist = drm_alloc(count * sizeof(*entry->buflist),
+				   DRM_MEM_BUFS);
+	if (!entry->buflist) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM;
+	}
+	memset(entry->buflist, 0, count * sizeof(*entry->buflist));
+
+	entry->buf_size   = size;
+	entry->page_order = page_order;
+	offset            = 0;
+
+	for (offset = 0;
+	     entry->buf_count < count;
+	     offset += alignment, ++entry->buf_count) {
+		buf          = &entry->buflist[entry->buf_count];
+		buf->idx     = dma->buf_count + entry->buf_count;
+		buf->total   = alignment;
+		buf->order   = order;
+		buf->used    = 0;
+		buf->offset  = (dma->byte_count + offset);
+		buf->address = (void *)(agp_offset + offset);
+		buf->next    = NULL;
+		buf->waiting = 0;
+		buf->pending = 0;
+		init_waitqueue_head(&buf->dma_wait);
+		buf->pid     = 0;
+
+		buf->dev_priv_size = sizeof(drm_radeon_buf_priv_t);
+		buf->dev_private   = drm_alloc(sizeof(drm_radeon_buf_priv_t),
+					       DRM_MEM_BUFS);
+                if (!buf->dev_private) {
+                        up(&dev->struct_sem);
+                        atomic_dec(&dev->buf_alloc);
+                        return -ENOMEM;
+                }
+		memset(buf->dev_private, 0, buf->dev_priv_size);
+
+#if DRM_DMA_HISTOGRAM
+		buf->time_queued     = 0;
+		buf->time_dispatched = 0;
+		buf->time_completed  = 0;
+		buf->time_freed      = 0;
+#endif
+
+		byte_count += PAGE_SIZE << page_order;
+
+		DRM_DEBUG("buffer %d @ %p\n",
+			  entry->buf_count, buf->address);
+	}
+
+	DRM_DEBUG("byte_count: %d\n", byte_count);
+
+	dma->buflist = drm_realloc(dma->buflist,
+				   dma->buf_count * sizeof(*dma->buflist),
+				   (dma->buf_count + entry->buf_count)
+				   * sizeof(*dma->buflist),
+				   DRM_MEM_BUFS);
+	for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++)
+		dma->buflist[i] = &entry->buflist[i - dma->buf_count];
+
+	dma->buf_count  += entry->buf_count;
+	dma->byte_count += byte_count;
+
+	drm_freelist_create(&entry->freelist, entry->buf_count);
+	for (i = 0; i < entry->buf_count; i++) {
+		drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]);
+	}
+
+	up(&dev->struct_sem);
+
+	request.count = entry->buf_count;
+	request.size  = size;
+
+	if (copy_to_user((drm_buf_desc_t *)arg, &request, sizeof(request)))
+		return -EFAULT;
+
+	dma->flags = _DRM_DMA_USE_AGP;
+
+	atomic_dec(&dev->buf_alloc);
+	return 0;
+}
+#endif
+
+int radeon_addbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		   unsigned long arg)
+{
+	drm_file_t		*priv		= filp->private_data;
+	drm_device_t		*dev		= priv->dev;
+	drm_radeon_private_t	*dev_priv	= dev->dev_private;
+	drm_buf_desc_t		request;
+
+	if (!dev_priv || dev_priv->is_pci) return -EINVAL;
+
+	if (copy_from_user(&request, (drm_buf_desc_t *)arg, sizeof(request)))
+		return -EFAULT;
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	if (request.flags & _DRM_AGP_BUFFER)
+		return radeon_addbufs_agp(inode, filp, cmd, arg);
+	else
+#endif
+		return -EINVAL;
+}
+
+int radeon_mapbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		   unsigned long arg)
+{
+	drm_file_t		*priv		= filp->private_data;
+	drm_device_t		*dev		= priv->dev;
+	drm_radeon_private_t	*dev_priv	= dev->dev_private;
+	drm_device_dma_t	*dma		= dev->dma;
+	int			 retcode	= 0;
+	const int		 zero		= 0;
+	unsigned long		 virtual;
+	unsigned long		 address;
+	drm_buf_map_t		 request;
+	int			 i;
+
+	if (!dma || !dev_priv || dev_priv->is_pci) return -EINVAL;
+
+	DRM_DEBUG("\n");
+
+	spin_lock(&dev->count_lock);
+	if (atomic_read(&dev->buf_alloc)) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	++dev->buf_use;		/* Can't allocate more after this call */
+	spin_unlock(&dev->count_lock);
+
+	if (copy_from_user(&request, (drm_buf_map_t *)arg, sizeof(request)))
+		return -EFAULT;
+
+	if (request.count >= dma->buf_count) {
+		if (dma->flags & _DRM_DMA_USE_AGP) {
+			drm_map_t *map;
+
+			map = dev_priv->buffers;
+			if (!map) {
+				retcode = -EINVAL;
+				goto done;
+			}
+
+			down_write(&current->mm->mmap_sem);
+			virtual = do_mmap(filp, 0, map->size,
+					  PROT_READ|PROT_WRITE,
+					  MAP_SHARED,
+					  (unsigned long)map->offset);
+			up_write(&current->mm->mmap_sem);
+		} else {
+			down_write(&current->mm->mmap_sem);
+			virtual = do_mmap(filp, 0, dma->byte_count,
+					  PROT_READ|PROT_WRITE, MAP_SHARED, 0);
+			up_write(&current->mm->mmap_sem);
+		}
+		if (virtual > -1024UL) {
+			/* Real error */
+			retcode = (signed long)virtual;
+			goto done;
+		}
+		request.virtual = (void *)virtual;
+
+		for (i = 0; i < dma->buf_count; i++) {
+			if (copy_to_user(&request.list[i].idx,
+					 &dma->buflist[i]->idx,
+					 sizeof(request.list[0].idx))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			if (copy_to_user(&request.list[i].total,
+					 &dma->buflist[i]->total,
+					 sizeof(request.list[0].total))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			if (copy_to_user(&request.list[i].used,
+					 &zero,
+					 sizeof(zero))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			address = virtual + dma->buflist[i]->offset;
+			if (copy_to_user(&request.list[i].address,
+					 &address,
+					 sizeof(address))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+		}
+	}
+ done:
+	request.count = dma->buf_count;
+	DRM_DEBUG("%d buffers, retcode = %d\n", request.count, retcode);
+
+	if (copy_to_user((drm_buf_map_t *)arg, &request, sizeof(request)))
+		return -EFAULT;
+
+	return retcode;
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/radeon_context.c linux/drivers/char/drm-4.0/radeon_context.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/radeon_context.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/radeon_context.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,215 @@
+/* radeon_context.c -- IOCTLs for Radeon contexts -*- linux-c -*-
+ *
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Fremont, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Author: Kevin E. Martin <martin@valinux.com>
+ *         Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "radeon_drv.h"
+
+extern drm_ctx_t radeon_res_ctx;
+
+static int radeon_alloc_queue(drm_device_t *dev)
+{
+	return drm_ctxbitmap_next(dev);
+}
+
+int radeon_context_switch(drm_device_t *dev, int old, int new)
+{
+        char        buf[64];
+
+        atomic_inc(&dev->total_ctx);
+
+        if (test_and_set_bit(0, &dev->context_flag)) {
+                DRM_ERROR("Reentering -- FIXME\n");
+                return -EBUSY;
+        }
+
+#if DRM_DMA_HISTOGRAM
+        dev->ctx_start = get_cycles();
+#endif
+
+        DRM_DEBUG("Context switch from %d to %d\n", old, new);
+
+        if (new == dev->last_context) {
+                clear_bit(0, &dev->context_flag);
+                return 0;
+        }
+
+        if (drm_flags & DRM_FLAG_NOCTX) {
+                radeon_context_switch_complete(dev, new);
+        } else {
+                sprintf(buf, "C %d %d\n", old, new);
+                drm_write_string(dev, buf);
+        }
+
+        return 0;
+}
+
+int radeon_context_switch_complete(drm_device_t *dev, int new)
+{
+        dev->last_context = new;  /* PRE/POST: This is the _only_ writer. */
+        dev->last_switch  = jiffies;
+
+        if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+                DRM_ERROR("Lock isn't held after context switch\n");
+        }
+
+				/* If a context switch is ever initiated
+                                   when the kernel holds the lock, release
+                                   that lock here. */
+#if DRM_DMA_HISTOGRAM
+        atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles()
+                                                      - dev->ctx_start)]);
+
+#endif
+        clear_bit(0, &dev->context_flag);
+        wake_up(&dev->context_wait);
+
+        return 0;
+}
+
+
+int radeon_resctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_ctx_res_t	res;
+	drm_ctx_t	ctx;
+	int		i;
+
+	DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS);
+	if (copy_from_user(&res, (drm_ctx_res_t *)arg, sizeof(res)))
+		return -EFAULT;
+	if (res.count >= DRM_RESERVED_CONTEXTS) {
+		memset(&ctx, 0, sizeof(ctx));
+		for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
+			ctx.handle = i;
+			if (copy_to_user(&res.contexts[i], &i, sizeof(i)))
+				return -EFAULT;
+		}
+	}
+	res.count = DRM_RESERVED_CONTEXTS;
+	if (copy_to_user((drm_ctx_res_t *)arg, &res, sizeof(res)))
+		return -EFAULT;
+	return 0;
+}
+
+
+int radeon_addctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	if ((ctx.handle = radeon_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) {
+				/* Skip kernel's context and get a new one. */
+		ctx.handle = radeon_alloc_queue(dev);
+	}
+	DRM_DEBUG("%d\n", ctx.handle);
+	if (ctx.handle == -1) {
+		DRM_DEBUG("Not enough free contexts.\n");
+				/* Should this return -EBUSY instead? */
+		return -ENOMEM;
+	}
+
+	if (copy_to_user((drm_ctx_t *)arg, &ctx, sizeof(ctx)))
+		return -EFAULT;
+	return 0;
+}
+
+int radeon_modctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_ctx_t ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx)))
+		return -EFAULT;
+	if (ctx.flags==_DRM_CONTEXT_PRESERVED)
+		radeon_res_ctx.handle=ctx.handle;
+	return 0;
+}
+
+int radeon_getctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_ctx_t ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx)))
+		return -EFAULT;
+	/* This is 0, because we don't hanlde any context flags */
+	ctx.flags = 0;
+	if (copy_to_user((drm_ctx_t*)arg, &ctx, sizeof(ctx)))
+		return -EFAULT;
+	return 0;
+}
+
+int radeon_switchctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		     unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	return radeon_context_switch(dev, dev->last_context, ctx.handle);
+}
+
+int radeon_newctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	radeon_context_switch_complete(dev, ctx.handle);
+
+	return 0;
+}
+
+int radeon_rmctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	drm_ctxbitmap_free(dev, ctx.handle);
+
+	return 0;
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/radeon_cp.c linux/drivers/char/drm-4.0/radeon_cp.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/radeon_cp.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/radeon_cp.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,1314 @@
+/* radeon_cp.c -- CP support for Radeon -*- linux-c -*-
+ *
+ * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Fremont, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Kevin E. Martin <martin@valinux.com>
+ *   Gareth Hughes <gareth@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "radeon_drv.h"
+
+#include <linux/interrupt.h>	/* For task queue support */
+#include <linux/delay.h>
+
+#define RADEON_FIFO_DEBUG	0
+
+
+/* CP microcode (from ATI) */
+static u32 radeon_cp_microcode[][2] = {
+	{ 0x21007000, 0000000000 },
+	{ 0x20007000, 0000000000 },
+	{ 0x000000b4, 0x00000004 },
+	{ 0x000000b8, 0x00000004 },
+	{ 0x6f5b4d4c, 0000000000 },
+	{ 0x4c4c427f, 0000000000 },
+	{ 0x5b568a92, 0000000000 },
+	{ 0x4ca09c6d, 0000000000 },
+	{ 0xad4c4c4c, 0000000000 },
+	{ 0x4ce1af3d, 0000000000 },
+	{ 0xd8afafaf, 0000000000 },
+	{ 0xd64c4cdc, 0000000000 },
+	{ 0x4cd10d10, 0000000000 },
+	{ 0x000f0000, 0x00000016 },
+	{ 0x362f242d, 0000000000 },
+	{ 0x00000012, 0x00000004 },
+	{ 0x000f0000, 0x00000016 },
+	{ 0x362f282d, 0000000000 },
+	{ 0x000380e7, 0x00000002 },
+	{ 0x04002c97, 0x00000002 },
+	{ 0x000f0001, 0x00000016 },
+	{ 0x333a3730, 0000000000 },
+	{ 0x000077ef, 0x00000002 },
+	{ 0x00061000, 0x00000002 },
+	{ 0x00000021, 0x0000001a },
+	{ 0x00004000, 0x0000001e },
+	{ 0x00061000, 0x00000002 },
+	{ 0x00000021, 0x0000001a },
+	{ 0x00004000, 0x0000001e },
+	{ 0x00061000, 0x00000002 },
+	{ 0x00000021, 0x0000001a },
+	{ 0x00004000, 0x0000001e },
+	{ 0x00000017, 0x00000004 },
+	{ 0x0003802b, 0x00000002 },
+	{ 0x040067e0, 0x00000002 },
+	{ 0x00000017, 0x00000004 },
+	{ 0x000077e0, 0x00000002 },
+	{ 0x00065000, 0x00000002 },
+	{ 0x000037e1, 0x00000002 },
+	{ 0x040067e1, 0x00000006 },
+	{ 0x000077e0, 0x00000002 },
+	{ 0x000077e1, 0x00000002 },
+	{ 0x000077e1, 0x00000006 },
+	{ 0xffffffff, 0000000000 },
+	{ 0x10000000, 0000000000 },
+	{ 0x0003802b, 0x00000002 },
+	{ 0x040067e0, 0x00000006 },
+	{ 0x00007675, 0x00000002 },
+	{ 0x00007676, 0x00000002 },
+	{ 0x00007677, 0x00000002 },
+	{ 0x00007678, 0x00000006 },
+	{ 0x0003802c, 0x00000002 },
+	{ 0x04002676, 0x00000002 },
+	{ 0x00007677, 0x00000002 },
+	{ 0x00007678, 0x00000006 },
+	{ 0x0000002f, 0x00000018 },
+	{ 0x0000002f, 0x00000018 },
+	{ 0000000000, 0x00000006 },
+	{ 0x00000030, 0x00000018 },
+	{ 0x00000030, 0x00000018 },
+	{ 0000000000, 0x00000006 },
+	{ 0x01605000, 0x00000002 },
+	{ 0x00065000, 0x00000002 },
+	{ 0x00098000, 0x00000002 },
+	{ 0x00061000, 0x00000002 },
+	{ 0x64c0603e, 0x00000004 },
+	{ 0x000380e6, 0x00000002 },
+	{ 0x040025c5, 0x00000002 },
+	{ 0x00080000, 0x00000016 },
+	{ 0000000000, 0000000000 },
+	{ 0x0400251d, 0x00000002 },
+	{ 0x00007580, 0x00000002 },
+	{ 0x00067581, 0x00000002 },
+	{ 0x04002580, 0x00000002 },
+	{ 0x00067581, 0x00000002 },
+	{ 0x00000049, 0x00000004 },
+	{ 0x00005000, 0000000000 },
+	{ 0x000380e6, 0x00000002 },
+	{ 0x040025c5, 0x00000002 },
+	{ 0x00061000, 0x00000002 },
+	{ 0x0000750e, 0x00000002 },
+	{ 0x00019000, 0x00000002 },
+	{ 0x00011055, 0x00000014 },
+	{ 0x00000055, 0x00000012 },
+	{ 0x0400250f, 0x00000002 },
+	{ 0x0000504f, 0x00000004 },
+	{ 0x000380e6, 0x00000002 },
+	{ 0x040025c5, 0x00000002 },
+	{ 0x00007565, 0x00000002 },
+	{ 0x00007566, 0x00000002 },
+	{ 0x00000058, 0x00000004 },
+	{ 0x000380e6, 0x00000002 },
+	{ 0x040025c5, 0x00000002 },
+	{ 0x01e655b4, 0x00000002 },
+	{ 0x4401b0e4, 0x00000002 },
+	{ 0x01c110e4, 0x00000002 },
+	{ 0x26667066, 0x00000018 },
+	{ 0x040c2565, 0x00000002 },
+	{ 0x00000066, 0x00000018 },
+	{ 0x04002564, 0x00000002 },
+	{ 0x00007566, 0x00000002 },
+	{ 0x0000005d, 0x00000004 },
+	{ 0x00401069, 0x00000008 },
+	{ 0x00101000, 0x00000002 },
+	{ 0x000d80ff, 0x00000002 },
+	{ 0x0080006c, 0x00000008 },
+	{ 0x000f9000, 0x00000002 },
+	{ 0x000e00ff, 0x00000002 },
+	{ 0000000000, 0x00000006 },
+	{ 0x0000008f, 0x00000018 },
+	{ 0x0000005b, 0x00000004 },
+	{ 0x000380e6, 0x00000002 },
+	{ 0x040025c5, 0x00000002 },
+	{ 0x00007576, 0x00000002 },
+	{ 0x00065000, 0x00000002 },
+	{ 0x00009000, 0x00000002 },
+	{ 0x00041000, 0x00000002 },
+	{ 0x0c00350e, 0x00000002 },
+	{ 0x00049000, 0x00000002 },
+	{ 0x00051000, 0x00000002 },
+	{ 0x01e785f8, 0x00000002 },
+	{ 0x00200000, 0x00000002 },
+	{ 0x0060007e, 0x0000000c },
+	{ 0x00007563, 0x00000002 },
+	{ 0x006075f0, 0x00000021 },
+	{ 0x20007073, 0x00000004 },
+	{ 0x00005073, 0x00000004 },
+	{ 0x000380e6, 0x00000002 },
+	{ 0x040025c5, 0x00000002 },
+	{ 0x00007576, 0x00000002 },
+	{ 0x00007577, 0x00000002 },
+	{ 0x0000750e, 0x00000002 },
+	{ 0x0000750f, 0x00000002 },
+	{ 0x00a05000, 0x00000002 },
+	{ 0x00600083, 0x0000000c },
+	{ 0x006075f0, 0x00000021 },
+	{ 0x000075f8, 0x00000002 },
+	{ 0x00000083, 0x00000004 },
+	{ 0x000a750e, 0x00000002 },
+	{ 0x000380e6, 0x00000002 },
+	{ 0x040025c5, 0x00000002 },
+	{ 0x0020750f, 0x00000002 },
+	{ 0x00600086, 0x00000004 },
+	{ 0x00007570, 0x00000002 },
+	{ 0x00007571, 0x00000002 },
+	{ 0x00007572, 0x00000006 },
+	{ 0x000380e6, 0x00000002 },
+	{ 0x040025c5, 0x00000002 },
+	{ 0x00005000, 0x00000002 },
+	{ 0x00a05000, 0x00000002 },
+	{ 0x00007568, 0x00000002 },
+	{ 0x00061000, 0x00000002 },
+	{ 0x00000095, 0x0000000c },
+	{ 0x00058000, 0x00000002 },
+	{ 0x0c607562, 0x00000002 },
+	{ 0x00000097, 0x00000004 },
+	{ 0x000380e6, 0x00000002 },
+	{ 0x040025c5, 0x00000002 },
+	{ 0x00600096, 0x00000004 },
+	{ 0x400070e5, 0000000000 },
+	{ 0x000380e6, 0x00000002 },
+	{ 0x040025c5, 0x00000002 },
+	{ 0x000380e5, 0x00000002 },
+	{ 0x000000a8, 0x0000001c },
+	{ 0x000650aa, 0x00000018 },
+	{ 0x040025bb, 0x00000002 },
+	{ 0x000610ab, 0x00000018 },
+	{ 0x040075bc, 0000000000 },
+	{ 0x000075bb, 0x00000002 },
+	{ 0x000075bc, 0000000000 },
+	{ 0x00090000, 0x00000006 },
+	{ 0x00090000, 0x00000002 },
+	{ 0x000d8002, 0x00000006 },
+	{ 0x00007832, 0x00000002 },
+	{ 0x00005000, 0x00000002 },
+	{ 0x000380e7, 0x00000002 },
+	{ 0x04002c97, 0x00000002 },
+	{ 0x00007820, 0x00000002 },
+	{ 0x00007821, 0x00000002 },
+	{ 0x00007800, 0000000000 },
+	{ 0x01200000, 0x00000002 },
+	{ 0x20077000, 0x00000002 },
+	{ 0x01200000, 0x00000002 },
+	{ 0x20007000, 0x00000002 },
+	{ 0x00061000, 0x00000002 },
+	{ 0x0120751b, 0x00000002 },
+	{ 0x8040750a, 0x00000002 },
+	{ 0x8040750b, 0x00000002 },
+	{ 0x00110000, 0x00000002 },
+	{ 0x000380e5, 0x00000002 },
+	{ 0x000000c6, 0x0000001c },
+	{ 0x000610ab, 0x00000018 },
+	{ 0x844075bd, 0x00000002 },
+	{ 0x000610aa, 0x00000018 },
+	{ 0x840075bb, 0x00000002 },
+	{ 0x000610ab, 0x00000018 },
+	{ 0x844075bc, 0x00000002 },
+	{ 0x000000c9, 0x00000004 },
+	{ 0x804075bd, 0x00000002 },
+	{ 0x800075bb, 0x00000002 },
+	{ 0x804075bc, 0x00000002 },
+	{ 0x00108000, 0x00000002 },
+	{ 0x01400000, 0x00000002 },
+	{ 0x006000cd, 0x0000000c },
+	{ 0x20c07000, 0x00000020 },
+	{ 0x000000cf, 0x00000012 },
+	{ 0x00800000, 0x00000006 },
+	{ 0x0080751d, 0x00000006 },
+	{ 0000000000, 0000000000 },
+	{ 0x0000775c, 0x00000002 },
+	{ 0x00a05000, 0x00000002 },
+	{ 0x00661000, 0x00000002 },
+	{ 0x0460275d, 0x00000020 },
+	{ 0x00004000, 0000000000 },
+	{ 0x01e00830, 0x00000002 },
+	{ 0x21007000, 0000000000 },
+	{ 0x6464614d, 0000000000 },
+	{ 0x69687420, 0000000000 },
+	{ 0x00000073, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0x00005000, 0x00000002 },
+	{ 0x000380d0, 0x00000002 },
+	{ 0x040025e0, 0x00000002 },
+	{ 0x000075e1, 0000000000 },
+	{ 0x00000001, 0000000000 },
+	{ 0x000380e0, 0x00000002 },
+	{ 0x04002394, 0x00000002 },
+	{ 0x00005000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0x00000008, 0000000000 },
+	{ 0x00000004, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+};
+
+
+#define DO_IOREMAP(_m) (_m)->handle = drm_ioremap((_m)->offset, (_m)->size)
+
+#define DO_IOREMAPFREE(_m)						\
+	do {								\
+		if ((_m)->handle && (_m)->size)				\
+			drm_ioremapfree((_m)->handle, (_m)->size);	\
+	} while (0)
+
+#define DO_FIND_MAP(_m, _o)						\
+	do {								\
+		int _i;							\
+		for (_i = 0; _i < dev->map_count; _i++) {		\
+			if (dev->maplist[_i]->offset == _o) {		\
+				_m = dev->maplist[_i];			\
+				break;					\
+			}						\
+		}							\
+	} while (0)
+
+
+int RADEON_READ_PLL(drm_device_t *dev, int addr)
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+
+	RADEON_WRITE8(RADEON_CLOCK_CNTL_INDEX, addr & 0x1f);
+	return RADEON_READ(RADEON_CLOCK_CNTL_DATA);
+}
+
+#if RADEON_FIFO_DEBUG
+static void radeon_status( drm_radeon_private_t *dev_priv )
+{
+	printk( "%s:\n", __FUNCTION__ );
+	printk( "RBBM_STATUS = 0x%08x\n",
+		(unsigned int)RADEON_READ( RADEON_RBBM_STATUS ) );
+	printk( "CP_RB_RTPR = 0x%08x\n",
+		(unsigned int)RADEON_READ( RADEON_CP_RB_RPTR ) );
+	printk( "CP_RB_WTPR = 0x%08x\n",
+		(unsigned int)RADEON_READ( RADEON_CP_RB_WPTR ) );
+}
+#endif
+
+
+/* ================================================================
+ * Engine, FIFO control
+ */
+
+static int radeon_do_pixcache_flush( drm_radeon_private_t *dev_priv )
+{
+	u32 tmp;
+	int i;
+
+	tmp  = RADEON_READ( RADEON_RB2D_DSTCACHE_CTLSTAT );
+	tmp |= RADEON_RB2D_DC_FLUSH_ALL;
+	RADEON_WRITE( RADEON_RB2D_DSTCACHE_CTLSTAT, tmp );
+
+	for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) {
+		if ( !(RADEON_READ( RADEON_RB2D_DSTCACHE_CTLSTAT )
+		       & RADEON_RB2D_DC_BUSY) ) {
+			return 0;
+		}
+		udelay( 1 );
+	}
+
+#if RADEON_FIFO_DEBUG
+	DRM_ERROR( "failed!\n" );
+	radeon_status( dev_priv );
+#endif
+	return -EBUSY;
+}
+
+static int radeon_do_wait_for_fifo( drm_radeon_private_t *dev_priv,
+				    int entries )
+{
+	int i;
+
+	for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) {
+		int slots = ( RADEON_READ( RADEON_RBBM_STATUS )
+			      & RADEON_RBBM_FIFOCNT_MASK );
+		if ( slots >= entries ) return 0;
+		udelay( 1 );
+	}
+
+#if RADEON_FIFO_DEBUG
+	DRM_ERROR( "failed!\n" );
+	radeon_status( dev_priv );
+#endif
+	return -EBUSY;
+}
+
+static int radeon_do_wait_for_idle( drm_radeon_private_t *dev_priv )
+{
+	int i, ret;
+
+	ret = radeon_do_wait_for_fifo( dev_priv, 64 );
+	if ( ret < 0 ) return ret;
+
+	for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) {
+		if ( !(RADEON_READ( RADEON_RBBM_STATUS )
+		       & RADEON_RBBM_ACTIVE) ) {
+			radeon_do_pixcache_flush( dev_priv );
+			return 0;
+		}
+		udelay( 1 );
+	}
+
+#if RADEON_FIFO_DEBUG
+	DRM_ERROR( "failed!\n" );
+	radeon_status( dev_priv );
+#endif
+	return -EBUSY;
+}
+
+
+/* ================================================================
+ * CP control, initialization
+ */
+
+/* Load the microcode for the CP */
+static void radeon_cp_load_microcode( drm_radeon_private_t *dev_priv )
+{
+	int i;
+
+	radeon_do_wait_for_idle( dev_priv );
+
+	RADEON_WRITE( RADEON_CP_ME_RAM_ADDR, 0 );
+	for ( i = 0 ; i < 256 ; i++ ) {
+		RADEON_WRITE( RADEON_CP_ME_RAM_DATAH,
+			      radeon_cp_microcode[i][1] );
+		RADEON_WRITE( RADEON_CP_ME_RAM_DATAL,
+			      radeon_cp_microcode[i][0] );
+	}
+}
+
+/* Flush any pending commands to the CP.  This should only be used just
+ * prior to a wait for idle, as it informs the engine that the command
+ * stream is ending.
+ */
+static void radeon_do_cp_flush( drm_radeon_private_t *dev_priv )
+{
+#if 0
+	u32 tmp;
+
+	tmp = RADEON_READ( RADEON_CP_RB_WPTR ) | (1 << 31);
+	RADEON_WRITE( RADEON_CP_RB_WPTR, tmp );
+#endif
+}
+
+/* Wait for the CP to go idle.
+ */
+int radeon_do_cp_idle( drm_radeon_private_t *dev_priv )
+{
+	RING_LOCALS;
+
+	BEGIN_RING( 6 );
+
+	RADEON_PURGE_CACHE();
+	RADEON_PURGE_ZCACHE();
+	RADEON_WAIT_UNTIL_IDLE();
+
+	ADVANCE_RING();
+
+	return radeon_do_wait_for_idle( dev_priv );
+}
+
+/* Start the Command Processor.
+ */
+static void radeon_do_cp_start( drm_radeon_private_t *dev_priv )
+{
+	RING_LOCALS;
+
+	radeon_do_wait_for_idle( dev_priv );
+
+	RADEON_WRITE( RADEON_CP_CSQ_CNTL, dev_priv->cp_mode );
+
+	dev_priv->cp_running = 1;
+
+	BEGIN_RING( 6 );
+
+	RADEON_PURGE_CACHE();
+	RADEON_PURGE_ZCACHE();
+	RADEON_WAIT_UNTIL_IDLE();
+
+	ADVANCE_RING();
+}
+
+/* Reset the Command Processor.  This will not flush any pending
+ * commands, so you must wait for the CP command stream to complete
+ * before calling this routine.
+ */
+static void radeon_do_cp_reset( drm_radeon_private_t *dev_priv )
+{
+	u32 cur_read_ptr;
+
+	cur_read_ptr = RADEON_READ( RADEON_CP_RB_RPTR );
+	RADEON_WRITE( RADEON_CP_RB_WPTR, cur_read_ptr );
+	*dev_priv->ring.head = cur_read_ptr;
+	dev_priv->ring.tail = cur_read_ptr;
+}
+
+/* Stop the Command Processor.  This will not flush any pending
+ * commands, so you must flush the command stream and wait for the CP
+ * to go idle before calling this routine.
+ */
+static void radeon_do_cp_stop( drm_radeon_private_t *dev_priv )
+{
+	RADEON_WRITE( RADEON_CP_CSQ_CNTL, RADEON_CSQ_PRIDIS_INDDIS );
+
+	dev_priv->cp_running = 0;
+}
+
+/* Reset the engine.  This will stop the CP if it is running.
+ */
+static int radeon_do_engine_reset( drm_device_t *dev )
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	u32 clock_cntl_index, mclk_cntl, rbbm_soft_reset;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	radeon_do_pixcache_flush( dev_priv );
+
+	clock_cntl_index = RADEON_READ( RADEON_CLOCK_CNTL_INDEX );
+	mclk_cntl = RADEON_READ_PLL( dev, RADEON_MCLK_CNTL );
+
+	/* FIXME: remove magic number here and in radeon ddx driver!!! */
+	RADEON_WRITE_PLL( RADEON_MCLK_CNTL, mclk_cntl | 0x003f00000 );
+
+	rbbm_soft_reset = RADEON_READ( RADEON_RBBM_SOFT_RESET );
+
+	RADEON_WRITE( RADEON_RBBM_SOFT_RESET, ( rbbm_soft_reset |
+						RADEON_SOFT_RESET_CP |
+						RADEON_SOFT_RESET_HI |
+						RADEON_SOFT_RESET_SE |
+						RADEON_SOFT_RESET_RE |
+						RADEON_SOFT_RESET_PP |
+						RADEON_SOFT_RESET_E2 |
+						RADEON_SOFT_RESET_RB |
+						RADEON_SOFT_RESET_HDP ) );
+	RADEON_READ( RADEON_RBBM_SOFT_RESET );
+	RADEON_WRITE( RADEON_RBBM_SOFT_RESET, ( rbbm_soft_reset &
+						~( RADEON_SOFT_RESET_CP |
+						   RADEON_SOFT_RESET_HI |
+						   RADEON_SOFT_RESET_SE |
+						   RADEON_SOFT_RESET_RE |
+						   RADEON_SOFT_RESET_PP |
+						   RADEON_SOFT_RESET_E2 |
+						   RADEON_SOFT_RESET_RB |
+						   RADEON_SOFT_RESET_HDP ) ) );
+	RADEON_READ( RADEON_RBBM_SOFT_RESET );
+
+
+	RADEON_WRITE_PLL( RADEON_MCLK_CNTL, mclk_cntl );
+	RADEON_WRITE( RADEON_CLOCK_CNTL_INDEX, clock_cntl_index );
+	RADEON_WRITE( RADEON_RBBM_SOFT_RESET,  rbbm_soft_reset );
+
+	/* Reset the CP ring */
+	radeon_do_cp_reset( dev_priv );
+
+	/* The CP is no longer running after an engine reset */
+	dev_priv->cp_running = 0;
+
+	/* Reset any pending vertex, indirect buffers */
+	radeon_freelist_reset( dev );
+
+	return 0;
+}
+
+static void radeon_cp_init_ring_buffer( drm_device_t *dev )
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	u32 ring_start, cur_read_ptr;
+	u32 tmp;
+
+	/* Initialize the memory controller */
+	RADEON_WRITE( RADEON_MC_FB_LOCATION,
+		      (dev_priv->agp_vm_start - 1) & 0xffff0000 );
+	RADEON_WRITE( RADEON_MC_AGP_LOCATION,
+		      (((dev_priv->agp_vm_start - 1 +
+			 dev_priv->agp_size) & 0xffff0000) |
+		       (dev_priv->agp_vm_start >> 16)) );
+
+	ring_start = (dev_priv->cp_ring->offset
+		      - dev->agp->base
+		      + dev_priv->agp_vm_start);
+
+	RADEON_WRITE( RADEON_CP_RB_BASE, ring_start );
+
+	/* Set the write pointer delay */
+	RADEON_WRITE( RADEON_CP_RB_WPTR_DELAY, 0 );
+
+	/* Initialize the ring buffer's read and write pointers */
+	cur_read_ptr = RADEON_READ( RADEON_CP_RB_RPTR );
+	RADEON_WRITE( RADEON_CP_RB_WPTR, cur_read_ptr );
+	*dev_priv->ring.head = cur_read_ptr;
+	dev_priv->ring.tail = cur_read_ptr;
+
+	RADEON_WRITE( RADEON_CP_RB_RPTR_ADDR, dev_priv->ring_rptr->offset );
+
+	/* Set ring buffer size */
+	RADEON_WRITE( RADEON_CP_RB_CNTL, dev_priv->ring.size_l2qw );
+
+	radeon_do_wait_for_idle( dev_priv );
+
+	/* Turn off PCI GART */
+	tmp = RADEON_READ( RADEON_AIC_CNTL ) & ~RADEON_PCIGART_TRANSLATE_EN;
+	RADEON_WRITE( RADEON_AIC_CNTL, tmp );
+
+	/* Turn on bus mastering */
+	tmp = RADEON_READ( RADEON_BUS_CNTL ) & ~RADEON_BUS_MASTER_DIS;
+	RADEON_WRITE( RADEON_BUS_CNTL, tmp );
+
+	/* Sync everything up */
+	RADEON_WRITE( RADEON_ISYNC_CNTL,
+		      (RADEON_ISYNC_ANY2D_IDLE3D |
+		       RADEON_ISYNC_ANY3D_IDLE2D |
+		       RADEON_ISYNC_WAIT_IDLEGUI |
+		       RADEON_ISYNC_CPSCRATCH_IDLEGUI) );
+}
+
+static int radeon_do_init_cp( drm_device_t *dev, drm_radeon_init_t *init )
+{
+	drm_radeon_private_t *dev_priv;
+        int i;
+
+	dev_priv = drm_alloc( sizeof(drm_radeon_private_t), DRM_MEM_DRIVER );
+	if ( dev_priv == NULL )
+		return -ENOMEM;
+	dev->dev_private = (void *)dev_priv;
+
+	memset( dev_priv, 0, sizeof(drm_radeon_private_t) );
+
+	dev_priv->is_pci = init->is_pci;
+
+	/* We don't support PCI cards until PCI GART is implemented.
+	 * Fail here so we can remove all checks for PCI cards around
+	 * the CP ring code.
+	 */
+	if ( dev_priv->is_pci ) {
+		drm_free( dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER );
+		dev->dev_private = NULL;
+		return -EINVAL;
+	}
+
+	dev_priv->usec_timeout = init->usec_timeout;
+	if ( dev_priv->usec_timeout < 1 ||
+	     dev_priv->usec_timeout > RADEON_MAX_USEC_TIMEOUT ) {
+		drm_free( dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER );
+		dev->dev_private = NULL;
+		return -EINVAL;
+	}
+
+	dev_priv->cp_mode = init->cp_mode;
+
+	/* Simple idle check.
+	 */
+	atomic_set( &dev_priv->idle_count, 0 );
+
+	/* We don't support anything other than bus-mastering ring mode,
+	 * but the ring can be in either AGP or PCI space for the ring
+	 * read pointer.
+	 */
+	if ( ( init->cp_mode != RADEON_CSQ_PRIBM_INDDIS ) &&
+	     ( init->cp_mode != RADEON_CSQ_PRIBM_INDBM ) ) {
+		drm_free( dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER );
+		dev->dev_private = NULL;
+		return -EINVAL;
+	}
+
+	switch ( init->fb_bpp ) {
+	case 16:
+		dev_priv->color_fmt = RADEON_COLOR_FORMAT_RGB565;
+		break;
+	case 32:
+	default:
+		dev_priv->color_fmt = RADEON_COLOR_FORMAT_ARGB8888;
+		break;
+	}
+	dev_priv->front_offset	= init->front_offset;
+	dev_priv->front_pitch	= init->front_pitch;
+	dev_priv->back_offset	= init->back_offset;
+	dev_priv->back_pitch	= init->back_pitch;
+
+	switch ( init->depth_bpp ) {
+	case 16:
+		dev_priv->depth_fmt = RADEON_DEPTH_FORMAT_16BIT_INT_Z;
+		break;
+	case 32:
+	default:
+		dev_priv->depth_fmt = RADEON_DEPTH_FORMAT_24BIT_INT_Z;
+		break;
+	}
+	dev_priv->depth_offset	= init->depth_offset;
+	dev_priv->depth_pitch	= init->depth_pitch;
+
+	dev_priv->front_pitch_offset = (((dev_priv->front_pitch/64) << 22) |
+					(dev_priv->front_offset >> 10));
+	dev_priv->back_pitch_offset = (((dev_priv->back_pitch/64) << 22) |
+				       (dev_priv->back_offset >> 10));
+	dev_priv->depth_pitch_offset = (((dev_priv->depth_pitch/64) << 22) |
+					(dev_priv->depth_offset >> 10));
+
+	/* Hardware state for depth clears.  Remove this if/when we no
+	 * longer clear the depth buffer with a 3D rectangle.  Hard-code
+	 * all values to prevent unwanted 3D state from slipping through
+	 * and screwing with the clear operation.
+	 */
+	dev_priv->depth_clear.rb3d_cntl = (RADEON_PLANE_MASK_ENABLE |
+					   RADEON_Z_ENABLE |
+					   (dev_priv->color_fmt << 10) |
+					   RADEON_ZBLOCK16);
+
+	dev_priv->depth_clear.rb3d_zstencilcntl = (dev_priv->depth_fmt |
+						   RADEON_Z_TEST_ALWAYS |
+						   RADEON_STENCIL_TEST_ALWAYS |
+						   RADEON_STENCIL_S_FAIL_KEEP |
+						   RADEON_STENCIL_ZPASS_KEEP |
+						   RADEON_STENCIL_ZFAIL_KEEP |
+						   RADEON_Z_WRITE_ENABLE);
+
+	dev_priv->depth_clear.se_cntl = (RADEON_FFACE_CULL_CW |
+					 RADEON_BFACE_SOLID |
+					 RADEON_FFACE_SOLID |
+					 RADEON_FLAT_SHADE_VTX_LAST |
+
+					 RADEON_DIFFUSE_SHADE_FLAT |
+					 RADEON_ALPHA_SHADE_FLAT |
+					 RADEON_SPECULAR_SHADE_FLAT |
+					 RADEON_FOG_SHADE_FLAT |
+
+					 RADEON_VTX_PIX_CENTER_OGL |
+					 RADEON_ROUND_MODE_TRUNC |
+					 RADEON_ROUND_PREC_8TH_PIX);
+
+	/* FIXME: We want multiple shared areas, including one shared
+	 * only by the X Server and kernel module.
+	 */
+	for ( i = 0 ; i < dev->map_count ; i++ ) {
+		if ( dev->maplist[i]->type == _DRM_SHM ) {
+			dev_priv->sarea = dev->maplist[i];
+			break;
+		}
+	}
+
+	DO_FIND_MAP( dev_priv->fb, init->fb_offset );
+	DO_FIND_MAP( dev_priv->mmio, init->mmio_offset );
+	DO_FIND_MAP( dev_priv->cp_ring, init->ring_offset );
+	DO_FIND_MAP( dev_priv->ring_rptr, init->ring_rptr_offset );
+	DO_FIND_MAP( dev_priv->buffers, init->buffers_offset );
+
+	if ( !dev_priv->is_pci ) {
+		DO_FIND_MAP( dev_priv->agp_textures,
+			     init->agp_textures_offset );
+	}
+
+	dev_priv->sarea_priv =
+		(drm_radeon_sarea_t *)((u8 *)dev_priv->sarea->handle +
+				       init->sarea_priv_offset);
+
+	DO_IOREMAP( dev_priv->cp_ring );
+	DO_IOREMAP( dev_priv->ring_rptr );
+	DO_IOREMAP( dev_priv->buffers );
+#if 0
+	if ( !dev_priv->is_pci ) {
+		DO_IOREMAP( dev_priv->agp_textures );
+	}
+#endif
+
+	dev_priv->agp_size = init->agp_size;
+	dev_priv->agp_vm_start = RADEON_READ( RADEON_CONFIG_APER_SIZE );
+	dev_priv->agp_buffers_offset = (dev_priv->buffers->offset
+					- dev->agp->base
+					+ dev_priv->agp_vm_start);
+
+	dev_priv->ring.head = ((__volatile__ u32 *)
+			       dev_priv->ring_rptr->handle);
+
+	dev_priv->ring.start = (u32 *)dev_priv->cp_ring->handle;
+	dev_priv->ring.end = ((u32 *)dev_priv->cp_ring->handle
+			      + init->ring_size / sizeof(u32));
+	dev_priv->ring.size = init->ring_size;
+	dev_priv->ring.size_l2qw = drm_order( init->ring_size / 8 );
+
+	dev_priv->ring.tail_mask =
+		(dev_priv->ring.size / sizeof(u32)) - 1;
+
+#if 0
+	/* Initialize the scratch register pointer.  This will cause
+	 * the scratch register values to be written out to memory
+	 * whenever they are updated.
+	 * FIXME: This doesn't quite work yet, so we're disabling it
+	 * for the release.
+	 */
+	RADEON_WRITE( RADEON_SCRATCH_ADDR, (dev_priv->ring_rptr->offset +
+					    RADEON_SCRATCH_REG_OFFSET) );
+	RADEON_WRITE( RADEON_SCRATCH_UMSK, 0x7 );
+#endif
+
+	dev_priv->scratch = ((__volatile__ u32 *)
+			     dev_priv->ring_rptr->handle +
+			     (RADEON_SCRATCH_REG_OFFSET / sizeof(u32)));
+
+	dev_priv->sarea_priv->last_frame = 0;
+	RADEON_WRITE( RADEON_LAST_FRAME_REG,
+		      dev_priv->sarea_priv->last_frame );
+
+	dev_priv->sarea_priv->last_dispatch = 0;
+	RADEON_WRITE( RADEON_LAST_DISPATCH_REG,
+		      dev_priv->sarea_priv->last_dispatch );
+
+	dev_priv->sarea_priv->last_clear = 0;
+	RADEON_WRITE( RADEON_LAST_CLEAR_REG,
+		      dev_priv->sarea_priv->last_clear );
+
+	radeon_cp_load_microcode( dev_priv );
+	radeon_cp_init_ring_buffer( dev );
+	radeon_do_engine_reset( dev );
+
+#if ROTATE_BUFS
+	dev_priv->last_buf = 0;
+#endif
+
+	return 0;
+}
+
+static int radeon_do_cleanup_cp( drm_device_t *dev )
+{
+	if ( dev->dev_private ) {
+		drm_radeon_private_t *dev_priv = dev->dev_private;
+
+		DO_IOREMAPFREE( dev_priv->cp_ring );
+		DO_IOREMAPFREE( dev_priv->ring_rptr );
+		DO_IOREMAPFREE( dev_priv->buffers );
+#if 0
+		if ( !dev_priv->is_pci ) {
+			DO_IOREMAPFREE( dev_priv->agp_textures );
+		}
+#endif
+
+		drm_free( dev->dev_private, sizeof(drm_radeon_private_t),
+			  DRM_MEM_DRIVER );
+		dev->dev_private = NULL;
+	}
+
+	return 0;
+}
+
+int radeon_cp_init( struct inode *inode, struct file *filp,
+		    unsigned int cmd, unsigned long arg )
+{
+        drm_file_t *priv = filp->private_data;
+        drm_device_t *dev = priv->dev;
+	drm_radeon_init_t init;
+
+	if ( copy_from_user( &init, (drm_radeon_init_t *)arg, sizeof(init) ) )
+		return -EFAULT;
+
+	switch ( init.func ) {
+	case RADEON_INIT_CP:
+		return radeon_do_init_cp( dev, &init );
+	case RADEON_CLEANUP_CP:
+		return radeon_do_cleanup_cp( dev );
+	}
+
+	return -EINVAL;
+}
+
+int radeon_cp_start( struct inode *inode, struct file *filp,
+		     unsigned int cmd, unsigned long arg )
+{
+        drm_file_t *priv = filp->private_data;
+        drm_device_t *dev = priv->dev;
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+	if ( dev_priv->cp_running ) {
+		DRM_DEBUG( "%s while CP running\n", __FUNCTION__ );
+		return 0;
+	}
+	if ( dev_priv->cp_mode == RADEON_CSQ_PRIDIS_INDDIS ) {
+		DRM_DEBUG( "%s called with bogus CP mode (%d)\n",
+			   __FUNCTION__, dev_priv->cp_mode );
+		return 0;
+	}
+
+	radeon_do_cp_start( dev_priv );
+
+	return 0;
+}
+
+/* Stop the CP.  The engine must have been idled before calling this
+ * routine.
+ */
+int radeon_cp_stop( struct inode *inode, struct file *filp,
+		    unsigned int cmd, unsigned long arg )
+{
+        drm_file_t *priv = filp->private_data;
+        drm_device_t *dev = priv->dev;
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	drm_radeon_cp_stop_t stop;
+	int ret;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &stop, (drm_radeon_init_t *)arg, sizeof(stop) ) )
+		return -EFAULT;
+
+	/* Flush any pending CP commands.  This ensures any outstanding
+	 * commands are exectuted by the engine before we turn it off.
+	 */
+	if ( stop.flush ) {
+		radeon_do_cp_flush( dev_priv );
+	}
+
+	/* If we fail to make the engine go idle, we return an error
+	 * code so that the DRM ioctl wrapper can try again.
+	 */
+	if ( stop.idle ) {
+		ret = radeon_do_cp_idle( dev_priv );
+		if ( ret < 0 ) return ret;
+	}
+
+	/* Finally, we can turn off the CP.  If the engine isn't idle,
+	 * we will get some dropped triangles as they won't be fully
+	 * rendered before the CP is shut down.
+	 */
+	radeon_do_cp_stop( dev_priv );
+
+	/* Reset the engine */
+	radeon_do_engine_reset( dev );
+
+	return 0;
+}
+
+/* Just reset the CP ring.  Called as part of an X Server engine reset.
+ */
+int radeon_cp_reset( struct inode *inode, struct file *filp,
+		     unsigned int cmd, unsigned long arg )
+{
+        drm_file_t *priv = filp->private_data;
+        drm_device_t *dev = priv->dev;
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+	if ( !dev_priv ) {
+		DRM_DEBUG( "%s called before init done\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	radeon_do_cp_reset( dev_priv );
+
+	/* The CP is no longer running after an engine reset */
+	dev_priv->cp_running = 0;
+
+	return 0;
+}
+
+int radeon_cp_idle( struct inode *inode, struct file *filp,
+		    unsigned int cmd, unsigned long arg )
+{
+        drm_file_t *priv = filp->private_data;
+        drm_device_t *dev = priv->dev;
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	return radeon_do_cp_idle( dev_priv );
+}
+
+int radeon_engine_reset( struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg )
+{
+        drm_file_t *priv = filp->private_data;
+        drm_device_t *dev = priv->dev;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	return radeon_do_engine_reset( dev );
+}
+
+
+/* ================================================================
+ * Fullscreen mode
+ */
+
+static int radeon_do_init_pageflip( drm_device_t *dev )
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	dev_priv->crtc_offset =      RADEON_READ( RADEON_CRTC_OFFSET );
+	dev_priv->crtc_offset_cntl = RADEON_READ( RADEON_CRTC_OFFSET_CNTL );
+
+	RADEON_WRITE( RADEON_CRTC_OFFSET, dev_priv->front_offset );
+	RADEON_WRITE( RADEON_CRTC_OFFSET_CNTL,
+		      dev_priv->crtc_offset_cntl |
+		      RADEON_CRTC_OFFSET_FLIP_CNTL );
+
+	dev_priv->page_flipping = 1;
+	dev_priv->current_page = 0;
+
+	return 0;
+}
+
+int radeon_do_cleanup_pageflip( drm_device_t *dev )
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	RADEON_WRITE( RADEON_CRTC_OFFSET,      dev_priv->crtc_offset );
+	RADEON_WRITE( RADEON_CRTC_OFFSET_CNTL, dev_priv->crtc_offset_cntl );
+
+	dev_priv->page_flipping = 0;
+	dev_priv->current_page = 0;
+
+	return 0;
+}
+
+int radeon_fullscreen( struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg )
+{
+        drm_file_t *priv = filp->private_data;
+        drm_device_t *dev = priv->dev;
+	drm_radeon_fullscreen_t fs;
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &fs, (drm_radeon_fullscreen_t *)arg,
+			     sizeof(fs) ) )
+		return -EFAULT;
+
+	switch ( fs.func ) {
+	case RADEON_INIT_FULLSCREEN:
+		return radeon_do_init_pageflip( dev );
+	case RADEON_CLEANUP_FULLSCREEN:
+		return radeon_do_cleanup_pageflip( dev );
+	}
+
+	return -EINVAL;
+}
+
+
+/* ================================================================
+ * Freelist management
+ */
+#define RADEON_BUFFER_USED	0xffffffff
+#define RADEON_BUFFER_FREE	0
+
+#if 0
+static int radeon_freelist_init( drm_device_t *dev )
+{
+	drm_device_dma_t *dma = dev->dma;
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	drm_buf_t *buf;
+	drm_radeon_buf_priv_t *buf_priv;
+	drm_radeon_freelist_t *entry;
+	int i;
+
+	dev_priv->head = drm_alloc( sizeof(drm_radeon_freelist_t),
+				    DRM_MEM_DRIVER );
+	if ( dev_priv->head == NULL )
+		return -ENOMEM;
+
+	memset( dev_priv->head, 0, sizeof(drm_radeon_freelist_t) );
+	dev_priv->head->age = RADEON_BUFFER_USED;
+
+	for ( i = 0 ; i < dma->buf_count ; i++ ) {
+		buf = dma->buflist[i];
+		buf_priv = buf->dev_private;
+
+		entry = drm_alloc( sizeof(drm_radeon_freelist_t),
+				   DRM_MEM_DRIVER );
+		if ( !entry ) return -ENOMEM;
+
+		entry->age = RADEON_BUFFER_FREE;
+		entry->buf = buf;
+		entry->prev = dev_priv->head;
+		entry->next = dev_priv->head->next;
+		if ( !entry->next )
+			dev_priv->tail = entry;
+
+		buf_priv->discard = 0;
+		buf_priv->dispatched = 0;
+		buf_priv->list_entry = entry;
+
+		dev_priv->head->next = entry;
+
+		if ( dev_priv->head->next )
+			dev_priv->head->next->prev = entry;
+	}
+
+	return 0;
+
+}
+#endif
+
+drm_buf_t *radeon_freelist_get( drm_device_t *dev )
+{
+	drm_device_dma_t *dma = dev->dma;
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	drm_radeon_buf_priv_t *buf_priv;
+	drm_buf_t *buf;
+	int i, t;
+#if ROTATE_BUFS
+	int start;
+#endif
+
+	/* FIXME: Optimize -- use freelist code */
+
+	for ( i = 0 ; i < dma->buf_count ; i++ ) {
+		buf = dma->buflist[i];
+		buf_priv = buf->dev_private;
+		if ( buf->pid == 0 ) {
+			DRM_DEBUG( "  ret buf=%d last=%d pid=0\n",
+				   buf->idx, dev_priv->last_buf );
+			return buf;
+		}
+		DRM_DEBUG( "    skipping buf=%d pid=%d\n",
+			   buf->idx, buf->pid );
+	}
+
+#if ROTATE_BUFS
+	if ( ++dev_priv->last_buf >= dma->buf_count )
+		dev_priv->last_buf = 0;
+	start = dev_priv->last_buf;
+#endif
+	for ( t = 0 ; t < dev_priv->usec_timeout ; t++ ) {
+#if 0
+		/* FIXME: Disable this for now */
+		u32 done_age = dev_priv->scratch[RADEON_LAST_DISPATCH];
+#else
+		u32 done_age = RADEON_READ( RADEON_LAST_DISPATCH_REG );
+#endif
+#if ROTATE_BUFS
+		for ( i = start ; i < dma->buf_count ; i++ ) {
+#else
+		for ( i = 0 ; i < dma->buf_count ; i++ ) {
+#endif
+			buf = dma->buflist[i];
+			buf_priv = buf->dev_private;
+			if ( buf->pending && buf_priv->age <= done_age ) {
+				/* The buffer has been processed, so it
+				 * can now be used.
+				 */
+				buf->pending = 0;
+				DRM_DEBUG( "  ret buf=%d last=%d age=%d done=%d\n", buf->idx, dev_priv->last_buf, buf_priv->age, done_age );
+				return buf;
+			}
+			DRM_DEBUG( "    skipping buf=%d age=%d done=%d\n",
+				   buf->idx, buf_priv->age,
+				   done_age );
+#if ROTATE_BUFS
+			start = 0;
+#endif
+		}
+		udelay( 1 );
+	}
+
+	DRM_ERROR( "returning NULL!\n" );
+	return NULL;
+}
+
+void radeon_freelist_reset( drm_device_t *dev )
+{
+	drm_device_dma_t *dma = dev->dma;
+#if ROTATE_BUFS
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+#endif
+	int i;
+
+#if ROTATE_BUFS
+	dev_priv->last_buf = 0;
+#endif
+	for ( i = 0 ; i < dma->buf_count ; i++ ) {
+		drm_buf_t *buf = dma->buflist[i];
+		drm_radeon_buf_priv_t *buf_priv = buf->dev_private;
+		buf_priv->age = 0;
+	}
+}
+
+
+/* ================================================================
+ * CP command submission
+ */
+
+int radeon_wait_ring( drm_radeon_private_t *dev_priv, int n )
+{
+	drm_radeon_ring_buffer_t *ring = &dev_priv->ring;
+	int i;
+
+	for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) {
+		ring->space = *ring->head - ring->tail;
+		if ( ring->space <= 0 )
+			ring->space += ring->size;
+
+		if ( ring->space >= n )
+			return 0;
+
+		udelay( 1 );
+	}
+
+	/* FIXME: This return value is ignored in the BEGIN_RING macro! */
+	DRM_ERROR( "failed!\n" );
+	return -EBUSY;
+}
+
+void radeon_update_ring_snapshot( drm_radeon_private_t *dev_priv )
+{
+	drm_radeon_ring_buffer_t *ring = &dev_priv->ring;
+
+	ring->space = *ring->head - ring->tail;
+	if ( ring->space == 0 )
+		atomic_inc( &dev_priv->idle_count );
+	if ( ring->space <= 0 )
+		ring->space += ring->size;
+}
+
+static int radeon_cp_get_buffers( drm_device_t *dev, drm_dma_t *d )
+{
+	int i;
+	drm_buf_t *buf;
+
+	for ( i = d->granted_count ; i < d->request_count ; i++ ) {
+		buf = radeon_freelist_get( dev );
+		if ( !buf ) return -EAGAIN;
+
+		buf->pid = current->pid;
+
+		if ( copy_to_user( &d->request_indices[i], &buf->idx,
+				   sizeof(buf->idx) ) )
+			return -EFAULT;
+		if ( copy_to_user( &d->request_sizes[i], &buf->total,
+				   sizeof(buf->total) ) )
+			return -EFAULT;
+
+		d->granted_count++;
+	}
+	return 0;
+}
+
+int radeon_cp_buffers( struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_device_dma_t *dma = dev->dma;
+	int ret = 0;
+	drm_dma_t d;
+
+	if ( copy_from_user( &d, (drm_dma_t *) arg, sizeof(d) ) )
+		return -EFAULT;
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	/* Please don't send us buffers.
+	 */
+	if ( d.send_count != 0 ) {
+		DRM_ERROR( "Process %d trying to send %d buffers via drmDMA\n",
+			   current->pid, d.send_count );
+		return -EINVAL;
+	}
+
+	/* We'll send you buffers.
+	 */
+	if ( d.request_count < 0 || d.request_count > dma->buf_count ) {
+		DRM_ERROR( "Process %d trying to get %d buffers (of %d max)\n",
+			   current->pid, d.request_count, dma->buf_count );
+		return -EINVAL;
+	}
+
+	d.granted_count = 0;
+
+	if ( d.request_count ) {
+		ret = radeon_cp_get_buffers( dev, &d );
+	}
+
+	if ( copy_to_user( (drm_dma_t *) arg, &d, sizeof(d) ) )
+		return -EFAULT;
+
+	return ret;
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/radeon_drm.h linux/drivers/char/drm-4.0/radeon_drm.h
--- linux-2.4.18-pre2/drivers/char/drm-4.0/radeon_drm.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/radeon_drm.h	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,325 @@
+/* radeon_drm.h -- Public header for the radeon driver -*- linux-c -*-
+ *
+ * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Fremont, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Kevin E. Martin <martin@valinux.com>
+ *    Gareth Hughes <gareth@valinux.com>
+ *
+ */
+
+#ifndef __RADEON_DRM_H__
+#define __RADEON_DRM_H__
+
+/* WARNING: If you change any of these defines, make sure to change the
+ * defines in the X server file (radeon_sarea.h)
+ */
+#ifndef __RADEON_SAREA_DEFINES__
+#define __RADEON_SAREA_DEFINES__
+
+/* What needs to be changed for the current vertex buffer?
+ */
+#define RADEON_UPLOAD_CONTEXT		0x00000001
+#define RADEON_UPLOAD_VERTFMT		0x00000002
+#define RADEON_UPLOAD_LINE		0x00000004
+#define RADEON_UPLOAD_BUMPMAP		0x00000008
+#define RADEON_UPLOAD_MASKS		0x00000010
+#define RADEON_UPLOAD_VIEWPORT		0x00000020
+#define RADEON_UPLOAD_SETUP		0x00000040
+#define RADEON_UPLOAD_TCL		0x00000080
+#define RADEON_UPLOAD_MISC		0x00000100
+#define RADEON_UPLOAD_TEX0		0x00000200
+#define RADEON_UPLOAD_TEX1		0x00000400
+#define RADEON_UPLOAD_TEX2		0x00000800
+#define RADEON_UPLOAD_TEX0IMAGES	0x00001000
+#define RADEON_UPLOAD_TEX1IMAGES	0x00002000
+#define RADEON_UPLOAD_TEX2IMAGES	0x00004000
+#define RADEON_UPLOAD_CLIPRECTS		0x00008000 /* handled client-side */
+#define RADEON_REQUIRE_QUIESCENCE	0x00010000
+#define RADEON_UPLOAD_ALL		0x0001ffff
+
+#define RADEON_FRONT			0x1
+#define RADEON_BACK			0x2
+#define RADEON_DEPTH			0x4
+
+/* Primitive types
+ */
+#define RADEON_POINTS			0x1
+#define RADEON_LINES			0x2
+#define RADEON_LINE_STRIP		0x3
+#define RADEON_TRIANGLES		0x4
+#define RADEON_TRIANGLE_FAN		0x5
+#define RADEON_TRIANGLE_STRIP		0x6
+
+/* Vertex/indirect buffer size
+ */
+#define RADEON_BUFFER_SIZE		16384
+
+/* Byte offsets for indirect buffer data
+ */
+#define RADEON_INDEX_PRIM_OFFSET	20
+#define RADEON_HOSTDATA_BLIT_OFFSET	32
+
+#define RADEON_SCRATCH_REG_OFFSET	32
+
+/* Keep these small for testing
+ */
+#define RADEON_NR_SAREA_CLIPRECTS	12
+
+/* There are 2 heaps (local/AGP).  Each region within a heap is a
+ * minimum of 64k, and there are at most 64 of them per heap.
+ */
+#define RADEON_LOCAL_TEX_HEAP		0
+#define RADEON_AGP_TEX_HEAP		1
+#define RADEON_NR_TEX_HEAPS		2
+#define RADEON_NR_TEX_REGIONS		64
+#define RADEON_LOG_TEX_GRANULARITY	16
+
+#define RADEON_MAX_TEXTURE_LEVELS	11
+#define RADEON_MAX_TEXTURE_UNITS	3
+
+#endif /* __RADEON_SAREA_DEFINES__ */
+
+typedef struct {
+	unsigned int red;
+	unsigned int green;
+	unsigned int blue;
+	unsigned int alpha;
+} radeon_color_regs_t;
+
+typedef struct {
+	/* Context state */
+	unsigned int pp_misc;				/* 0x1c14 */
+	unsigned int pp_fog_color;
+	unsigned int re_solid_color;
+	unsigned int rb3d_blendcntl;
+	unsigned int rb3d_depthoffset;
+	unsigned int rb3d_depthpitch;
+	unsigned int rb3d_zstencilcntl;
+
+	unsigned int pp_cntl;				/* 0x1c38 */
+	unsigned int rb3d_cntl;
+	unsigned int rb3d_coloroffset;
+	unsigned int re_width_height;
+	unsigned int rb3d_colorpitch;
+	unsigned int se_cntl;
+
+	/* Vertex format state */
+	unsigned int se_coord_fmt;			/* 0x1c50 */
+
+	/* Line state */
+	unsigned int re_line_pattern;			/* 0x1cd0 */
+	unsigned int re_line_state;
+
+	unsigned int se_line_width;			/* 0x1db8 */
+
+	/* Bumpmap state */
+	unsigned int pp_lum_matrix;			/* 0x1d00 */
+
+	unsigned int pp_rot_matrix_0;			/* 0x1d58 */
+	unsigned int pp_rot_matrix_1;
+
+	/* Mask state */
+	unsigned int rb3d_stencilrefmask;		/* 0x1d7c */
+	unsigned int rb3d_ropcntl;
+	unsigned int rb3d_planemask;
+
+	/* Viewport state */
+	unsigned int se_vport_xscale;			/* 0x1d98 */
+	unsigned int se_vport_xoffset;
+	unsigned int se_vport_yscale;
+	unsigned int se_vport_yoffset;
+	unsigned int se_vport_zscale;
+	unsigned int se_vport_zoffset;
+
+	/* Setup state */
+	unsigned int se_cntl_status;			/* 0x2140 */
+
+#ifdef TCL_ENABLE
+	/* TCL state */
+	radeon_color_regs_t se_tcl_material_emmissive;	/* 0x2210 */
+	radeon_color_regs_t se_tcl_material_ambient;
+	radeon_color_regs_t se_tcl_material_diffuse;
+	radeon_color_regs_t se_tcl_material_specular;
+	unsigned int se_tcl_shininess;
+	unsigned int se_tcl_output_vtx_fmt;
+	unsigned int se_tcl_output_vtx_sel;
+	unsigned int se_tcl_matrix_select_0;
+	unsigned int se_tcl_matrix_select_1;
+	unsigned int se_tcl_ucp_vert_blend_ctl;
+	unsigned int se_tcl_texture_proc_ctl;
+	unsigned int se_tcl_light_model_ctl;
+	unsigned int se_tcl_per_light_ctl[4];
+#endif
+
+	/* Misc state */
+	unsigned int re_top_left;			/* 0x26c0 */
+	unsigned int re_misc;
+} drm_radeon_context_regs_t;
+
+/* Setup registers for each texture unit
+ */
+typedef struct {
+	unsigned int pp_txfilter;
+	unsigned int pp_txformat;
+	unsigned int pp_txoffset;
+	unsigned int pp_txcblend;
+	unsigned int pp_txablend;
+	unsigned int pp_tfactor;
+
+	unsigned int pp_border_color;
+
+#ifdef CUBIC_ENABLE
+	unsigned int pp_cubic_faces;
+	unsigned int pp_cubic_offset[5];
+#endif
+} drm_radeon_texture_regs_t;
+
+typedef struct {
+	unsigned char next, prev;
+	unsigned char in_use;
+	int age;
+} drm_radeon_tex_region_t;
+
+typedef struct {
+	/* The channel for communication of state information to the kernel
+	 * on firing a vertex buffer.
+	 */
+	drm_radeon_context_regs_t context_state;
+	drm_radeon_texture_regs_t tex_state[RADEON_MAX_TEXTURE_UNITS];
+	unsigned int dirty;
+	unsigned int vertsize;
+	unsigned int vc_format;
+
+	/* The current cliprects, or a subset thereof.
+	 */
+	drm_clip_rect_t boxes[RADEON_NR_SAREA_CLIPRECTS];
+	unsigned int nbox;
+
+	/* Counters for client-side throttling of rendering clients.
+	 */
+	unsigned int last_frame;
+	unsigned int last_dispatch;
+	unsigned int last_clear;
+
+	drm_radeon_tex_region_t tex_list[RADEON_NR_TEX_HEAPS][RADEON_NR_TEX_REGIONS+1];
+	int tex_age[RADEON_NR_TEX_HEAPS];
+	int ctx_owner;
+} drm_radeon_sarea_t;
+
+
+/* WARNING: If you change any of these defines, make sure to change the
+ * defines in the Xserver file (xf86drmRadeon.h)
+ */
+typedef struct drm_radeon_init {
+	enum {
+		RADEON_INIT_CP    = 0x01,
+		RADEON_CLEANUP_CP = 0x02
+	} func;
+	int sarea_priv_offset;
+	int is_pci;
+	int cp_mode;
+	int agp_size;
+	int ring_size;
+	int usec_timeout;
+
+	unsigned int fb_bpp;
+	unsigned int front_offset, front_pitch;
+	unsigned int back_offset, back_pitch;
+	unsigned int depth_bpp;
+	unsigned int depth_offset, depth_pitch;
+
+	unsigned int fb_offset;
+	unsigned int mmio_offset;
+	unsigned int ring_offset;
+	unsigned int ring_rptr_offset;
+	unsigned int buffers_offset;
+	unsigned int agp_textures_offset;
+} drm_radeon_init_t;
+
+typedef struct drm_radeon_cp_stop {
+	int flush;
+	int idle;
+} drm_radeon_cp_stop_t;
+
+typedef struct drm_radeon_fullscreen {
+	enum {
+		RADEON_INIT_FULLSCREEN    = 0x01,
+		RADEON_CLEANUP_FULLSCREEN = 0x02
+	} func;
+} drm_radeon_fullscreen_t;
+
+#define CLEAR_X1	0
+#define CLEAR_Y1	1
+#define CLEAR_X2	2
+#define CLEAR_Y2	3
+#define CLEAR_DEPTH	4
+
+typedef struct drm_radeon_clear {
+	unsigned int flags;
+	int x, y, w, h;
+	unsigned int clear_color;
+	unsigned int clear_depth;
+	union {
+		float f[5];
+		unsigned int ui[5];
+	} rect;
+} drm_radeon_clear_t;
+
+typedef struct drm_radeon_vertex {
+	int prim;
+	int idx;			/* Index of vertex buffer */
+	int count;			/* Number of vertices in buffer */
+	int discard;			/* Client finished with buffer? */
+} drm_radeon_vertex_t;
+
+typedef struct drm_radeon_indices {
+	int prim;
+	int idx;
+	int start;
+	int end;
+	int discard;			/* Client finished with buffer? */
+} drm_radeon_indices_t;
+
+typedef struct drm_radeon_blit {
+	int idx;
+	int pitch;
+	int offset;
+	int format;
+	unsigned short x, y;
+	unsigned short width, height;
+} drm_radeon_blit_t;
+
+typedef struct drm_radeon_stipple {
+	unsigned int *mask;
+} drm_radeon_stipple_t;
+
+typedef struct drm_radeon_indirect {
+	int idx;
+	int start;
+	int end;
+	int discard;
+} drm_radeon_indirect_t;
+
+#endif
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/radeon_drv.c linux/drivers/char/drm-4.0/radeon_drv.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/radeon_drv.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/radeon_drv.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,703 @@
+/* radeon_drv.c -- ATI Radeon driver -*- linux-c -*-
+ *
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Fremont, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Kevin E. Martin <martin@valinux.com>
+ *          Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#include <linux/config.h>
+#include "drmP.h"
+#include "radeon_drv.h"
+
+#define RADEON_NAME		"radeon"
+#define RADEON_DESC		"ATI Radeon"
+#define RADEON_DATE		"20010105"
+#define RADEON_MAJOR		1
+#define RADEON_MINOR		0
+#define RADEON_PATCHLEVEL	0
+
+static drm_device_t	      radeon_device;
+drm_ctx_t	              radeon_res_ctx;
+
+static struct file_operations radeon_fops = {
+#if LINUX_VERSION_CODE >= 0x020400
+				/* This started being used during 2.4.0-test */
+	owner:   THIS_MODULE,
+#endif
+	open:	 radeon_open,
+	flush:	 drm_flush,
+	release: radeon_release,
+	ioctl:	 radeon_ioctl,
+	mmap:	 drm_mmap,
+	read:	 drm_read,
+	fasync:	 drm_fasync,
+	poll:	 drm_poll,
+};
+
+static struct miscdevice      radeon_misc = {
+	minor: MISC_DYNAMIC_MINOR,
+	name:  RADEON_NAME,
+	fops:  &radeon_fops,
+};
+
+static drm_ioctl_desc_t	      radeon_ioctls[] = {
+	[DRM_IOCTL_NR(DRM_IOCTL_VERSION)]       = { radeon_version,	0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)]    = { drm_getunique,	0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)]     = { drm_getmagic,	0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)]     = { drm_irq_busid,	0, 1 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)]    = { drm_setunique,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_BLOCK)]	        = { drm_block,		1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)]       = { drm_unblock,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)]    = { drm_authmagic,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)]       = { drm_addmap,		1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)]      = { radeon_addbufs,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)]     = { drm_markbufs,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)]     = { drm_infobufs,	1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MAP_BUFS)]      = { radeon_mapbufs,	1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)]     = { drm_freebufs,	1, 0 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)]       = { radeon_addctx,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)]        = { radeon_rmctx,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)]       = { radeon_modctx,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)]       = { radeon_getctx,	1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)]    = { radeon_switchctx,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)]       = { radeon_newctx,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)]       = { radeon_resctx,	1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)]      = { drm_adddraw,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)]       = { drm_rmdraw,		1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_DMA)]	        = { radeon_cp_buffers,	1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_LOCK)]	        = { radeon_lock,	1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)]        = { radeon_unlock,	1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FINISH)]        = { drm_finish,		1, 0 },
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)]   = { drm_agp_acquire,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)]   = { drm_agp_release,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)]    = { drm_agp_enable,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)]      = { drm_agp_info,	1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)]     = { drm_agp_alloc,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)]      = { drm_agp_free,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)]      = { drm_agp_bind,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)]    = { drm_agp_unbind,	1, 1 },
+#endif
+
+	[DRM_IOCTL_NR(DRM_IOCTL_RADEON_CP_INIT)]  = { radeon_cp_init,   1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RADEON_CP_START)] = { radeon_cp_start,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RADEON_CP_STOP)]  = { radeon_cp_stop,   1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RADEON_CP_RESET)] = { radeon_cp_reset,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RADEON_CP_IDLE)]  = { radeon_cp_idle,   1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RADEON_RESET)] = { radeon_engine_reset, 1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RADEON_FULLSCREEN)] = { radeon_fullscreen, 1, 0 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_RADEON_SWAP)]    = { radeon_cp_swap,    1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RADEON_CLEAR)]   = { radeon_cp_clear,   1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RADEON_VERTEX)]  = { radeon_cp_vertex,  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RADEON_INDICES)] = { radeon_cp_indices, 1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RADEON_BLIT)]    = { radeon_cp_blit,    1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RADEON_STIPPLE)] = { radeon_cp_stipple, 1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RADEON_INDIRECT)]= { radeon_cp_indirect,1, 1 },
+};
+#define RADEON_IOCTL_COUNT DRM_ARRAY_SIZE(radeon_ioctls)
+
+#ifdef MODULE
+static char		      *radeon = NULL;
+#endif
+
+MODULE_AUTHOR("VA Linux Systems, Inc.");
+MODULE_DESCRIPTION("radeon");
+MODULE_LICENSE("GPL and additional rights");
+MODULE_PARM(radeon, "s");
+
+#ifndef MODULE
+/* radeon_options is called by the kernel to parse command-line options
+ * passed via the boot-loader (e.g., LILO).  It calls the insmod option
+ * routine, drm_parse_drm.
+ */
+
+static int __init radeon_options(char *str)
+{
+	drm_parse_options(str);
+	return 1;
+}
+
+__setup("radeon=", radeon_options);
+#endif
+
+static int radeon_setup(drm_device_t *dev)
+{
+	int i;
+
+	atomic_set(&dev->ioctl_count, 0);
+	atomic_set(&dev->vma_count, 0);
+	dev->buf_use	  = 0;
+	atomic_set(&dev->buf_alloc, 0);
+
+	drm_dma_setup(dev);
+
+	atomic_set(&dev->total_open, 0);
+	atomic_set(&dev->total_close, 0);
+	atomic_set(&dev->total_ioctl, 0);
+	atomic_set(&dev->total_irq, 0);
+	atomic_set(&dev->total_ctx, 0);
+	atomic_set(&dev->total_locks, 0);
+	atomic_set(&dev->total_unlocks, 0);
+	atomic_set(&dev->total_contends, 0);
+	atomic_set(&dev->total_sleeps, 0);
+
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		dev->magiclist[i].head = NULL;
+		dev->magiclist[i].tail = NULL;
+	}
+	dev->maplist	    = NULL;
+	dev->map_count	    = 0;
+	dev->vmalist	    = NULL;
+	dev->lock.hw_lock   = NULL;
+	init_waitqueue_head(&dev->lock.lock_queue);
+	dev->queue_count    = 0;
+	dev->queue_reserved = 0;
+	dev->queue_slots    = 0;
+	dev->queuelist	    = NULL;
+	dev->irq	    = 0;
+	dev->context_flag   = 0;
+	dev->interrupt_flag = 0;
+	dev->dma_flag	    = 0;
+	dev->last_context   = 0;
+	dev->last_switch    = 0;
+	dev->last_checked   = 0;
+	init_timer(&dev->timer);
+	init_waitqueue_head(&dev->context_wait);
+
+	dev->ctx_start	    = 0;
+	dev->lck_start	    = 0;
+
+	dev->buf_rp	    = dev->buf;
+	dev->buf_wp	    = dev->buf;
+	dev->buf_end	    = dev->buf + DRM_BSZ;
+	dev->buf_async	    = NULL;
+	init_waitqueue_head(&dev->buf_readers);
+	init_waitqueue_head(&dev->buf_writers);
+
+	radeon_res_ctx.handle = -1;
+
+	DRM_DEBUG("\n");
+
+	/* The kernel's context could be created here, but is now created
+	   in drm_dma_enqueue.	This is more resource-efficient for
+	   hardware that does not do DMA, but may mean that
+	   drm_select_queue fails between the time the interrupt is
+	   initialized and the time the queues are initialized. */
+
+	return 0;
+}
+
+
+static int radeon_takedown(drm_device_t *dev)
+{
+	int		  i;
+	drm_magic_entry_t *pt, *next;
+	drm_map_t	  *map;
+	drm_vma_entry_t	  *vma, *vma_next;
+
+	DRM_DEBUG("\n");
+
+	down(&dev->struct_sem);
+	del_timer(&dev->timer);
+
+	if (dev->devname) {
+		drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER);
+		dev->devname = NULL;
+	}
+
+	if (dev->unique) {
+		drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER);
+		dev->unique = NULL;
+		dev->unique_len = 0;
+	}
+				/* Clear pid list */
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		for (pt = dev->magiclist[i].head; pt; pt = next) {
+			next = pt->next;
+			drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
+		}
+		dev->magiclist[i].head = dev->magiclist[i].tail = NULL;
+	}
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+				/* Clear AGP information */
+	if (dev->agp) {
+		drm_agp_mem_t *entry;
+		drm_agp_mem_t *nexte;
+
+				/* Remove AGP resources, but leave dev->agp
+                                   intact until radeon_cleanup is called. */
+		for (entry = dev->agp->memory; entry; entry = nexte) {
+			nexte = entry->next;
+			if (entry->bound) drm_unbind_agp(entry->memory);
+			drm_free_agp(entry->memory, entry->pages);
+			drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS);
+		}
+		dev->agp->memory = NULL;
+
+		if (dev->agp->acquired)	_drm_agp_release();
+
+		dev->agp->acquired = 0;
+		dev->agp->enabled  = 0;
+	}
+#endif
+
+				/* Clear vma list (only built for debugging) */
+	if (dev->vmalist) {
+		for (vma = dev->vmalist; vma; vma = vma_next) {
+			vma_next = vma->next;
+			drm_free(vma, sizeof(*vma), DRM_MEM_VMAS);
+		}
+		dev->vmalist = NULL;
+	}
+
+				/* Clear map area and mtrr information */
+	if (dev->maplist) {
+		for (i = 0; i < dev->map_count; i++) {
+			map = dev->maplist[i];
+			switch (map->type) {
+			case _DRM_REGISTERS:
+			case _DRM_FRAME_BUFFER:
+#ifdef CONFIG_MTRR
+				if (map->mtrr >= 0) {
+					int retcode;
+					retcode = mtrr_del(map->mtrr,
+							   map->offset,
+							   map->size);
+					DRM_DEBUG("mtrr_del = %d\n", retcode);
+				}
+#endif
+				drm_ioremapfree(map->handle, map->size);
+				break;
+			case _DRM_SHM:
+				drm_free_pages((unsigned long)map->handle,
+					       drm_order(map->size)
+					       - PAGE_SHIFT,
+					       DRM_MEM_SAREA);
+				break;
+			case _DRM_AGP:
+				/* Do nothing here, because this is all
+                                   handled in the AGP/GART driver. */
+				break;
+			}
+			drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+		}
+		drm_free(dev->maplist,
+			 dev->map_count * sizeof(*dev->maplist),
+			 DRM_MEM_MAPS);
+		dev->maplist   = NULL;
+		dev->map_count = 0;
+	}
+
+	drm_dma_takedown(dev);
+
+	dev->queue_count     = 0;
+	if (dev->lock.hw_lock) {
+		dev->lock.hw_lock    = NULL; /* SHM removed */
+		dev->lock.pid	     = 0;
+		wake_up_interruptible(&dev->lock.lock_queue);
+	}
+	up(&dev->struct_sem);
+
+	return 0;
+}
+
+/* radeon_init is called via init_module at module load time, or via
+ * linux/init/main.c (this is not currently supported). */
+
+static int __init radeon_init(void)
+{
+	int		      retcode;
+	drm_device_t	      *dev = &radeon_device;
+
+	DRM_DEBUG("\n");
+
+	memset((void *)dev, 0, sizeof(*dev));
+	dev->count_lock	  = SPIN_LOCK_UNLOCKED;
+	sema_init(&dev->struct_sem, 1);
+
+#ifdef MODULE
+	drm_parse_options(radeon);
+#endif
+
+	if ((retcode = misc_register(&radeon_misc))) {
+		DRM_ERROR("Cannot register \"%s\"\n", RADEON_NAME);
+		return retcode;
+	}
+	dev->device = MKDEV(MISC_MAJOR, radeon_misc.minor);
+	dev->name   = RADEON_NAME;
+
+	drm_mem_init();
+	drm_proc_init(dev);
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	dev->agp    = drm_agp_init();
+      	if (dev->agp == NULL) {
+	   	DRM_ERROR("Cannot initialize agpgart module.\n");
+	   	drm_proc_cleanup();
+	   	misc_deregister(&radeon_misc);
+	   	radeon_takedown(dev);
+	   	return -ENOMEM;
+	}
+
+#ifdef CONFIG_MTRR
+	dev->agp->agp_mtrr = mtrr_add(dev->agp->agp_info.aper_base,
+				      dev->agp->agp_info.aper_size*1024*1024,
+				      MTRR_TYPE_WRCOMB,
+				      1);
+#endif
+#endif
+
+	if((retcode = drm_ctxbitmap_init(dev))) {
+		DRM_ERROR("Cannot allocate memory for context bitmap.\n");
+		drm_proc_cleanup();
+		misc_deregister(&radeon_misc);
+		radeon_takedown(dev);
+		return retcode;
+	}
+
+	DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
+		 RADEON_NAME,
+		 RADEON_MAJOR,
+		 RADEON_MINOR,
+		 RADEON_PATCHLEVEL,
+		 RADEON_DATE,
+		 radeon_misc.minor);
+
+	return 0;
+}
+
+/* radeon_cleanup is called via cleanup_module at module unload time. */
+
+static void __exit radeon_cleanup(void)
+{
+	drm_device_t	      *dev = &radeon_device;
+
+	DRM_DEBUG("\n");
+
+	drm_proc_cleanup();
+	if (misc_deregister(&radeon_misc)) {
+		DRM_ERROR("Cannot unload module\n");
+	} else {
+		DRM_INFO("Module unloaded\n");
+	}
+	drm_ctxbitmap_cleanup(dev);
+	radeon_takedown(dev);
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	if (dev->agp) {
+		drm_agp_uninit();
+		drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS);
+		dev->agp = NULL;
+	}
+#endif
+}
+
+module_init(radeon_init);
+module_exit(radeon_cleanup);
+
+
+int radeon_version(struct inode *inode, struct file *filp, unsigned int cmd,
+		   unsigned long arg)
+{
+	drm_version_t version;
+	int	      len;
+
+	if (copy_from_user(&version,
+			   (drm_version_t *)arg,
+			   sizeof(version)))
+		return -EFAULT;
+
+#define DRM_COPY(name,value)				     \
+	len = strlen(value);				     \
+	if (len > name##_len) len = name##_len;		     \
+	name##_len = strlen(value);			     \
+	if (len && name) {				     \
+		if (copy_to_user(name, value, len))	     \
+			return -EFAULT;			     \
+	}
+
+	version.version_major	   = RADEON_MAJOR;
+	version.version_minor	   = RADEON_MINOR;
+	version.version_patchlevel = RADEON_PATCHLEVEL;
+
+	DRM_COPY(version.name, RADEON_NAME);
+	DRM_COPY(version.date, RADEON_DATE);
+	DRM_COPY(version.desc, RADEON_DESC);
+
+	if (copy_to_user((drm_version_t *)arg,
+			 &version,
+			 sizeof(version)))
+		return -EFAULT;
+	return 0;
+}
+
+int radeon_open(struct inode *inode, struct file *filp)
+{
+	drm_device_t  *dev    = &radeon_device;
+	int	      retcode = 0;
+
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	if (!(retcode = drm_open_helper(inode, filp, dev))) {
+#if LINUX_VERSION_CODE < 0x020333
+		MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+		atomic_inc(&dev->total_open);
+		spin_lock(&dev->count_lock);
+		if (!dev->open_count++) {
+			spin_unlock(&dev->count_lock);
+			return radeon_setup(dev);
+		}
+		spin_unlock(&dev->count_lock);
+	}
+
+	return retcode;
+}
+
+int radeon_release(struct inode *inode, struct file *filp)
+{
+	drm_file_t    *priv   = filp->private_data;
+	drm_device_t  *dev;
+	int	      retcode = 0;
+
+	lock_kernel();
+	dev = priv->dev;
+
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+
+	/* Force the cleanup of page flipping when required */
+	if ( dev->dev_private ) {
+		drm_radeon_private_t *dev_priv = dev->dev_private;
+		if ( dev_priv->page_flipping ) {
+			radeon_do_cleanup_pageflip( dev );
+		}
+	}
+
+	if (!(retcode = drm_release(inode, filp))) {
+#if LINUX_VERSION_CODE < 0x020333
+		MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+		atomic_inc(&dev->total_close);
+		spin_lock(&dev->count_lock);
+		if (!--dev->open_count) {
+			if (atomic_read(&dev->ioctl_count) || dev->blocked) {
+				DRM_ERROR("Device busy: %d %d\n",
+					  atomic_read(&dev->ioctl_count),
+					  dev->blocked);
+				spin_unlock(&dev->count_lock);
+				unlock_kernel();
+				return -EBUSY;
+			}
+			spin_unlock(&dev->count_lock);
+			unlock_kernel();
+			return radeon_takedown(dev);
+		}
+		spin_unlock(&dev->count_lock);
+	}
+
+	unlock_kernel();
+	return retcode;
+}
+
+/* radeon_ioctl is called whenever a process performs an ioctl on /dev/drm. */
+
+int radeon_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	int		 nr	 = DRM_IOCTL_NR(cmd);
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	int		 retcode = 0;
+	drm_ioctl_desc_t *ioctl;
+	drm_ioctl_t	 *func;
+
+	atomic_inc(&dev->ioctl_count);
+	atomic_inc(&dev->total_ioctl);
+	++priv->ioctl_count;
+
+	DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n",
+		  current->pid, cmd, nr, dev->device, priv->authenticated);
+
+	if (nr >= RADEON_IOCTL_COUNT) {
+		retcode = -EINVAL;
+	} else {
+		ioctl	  = &radeon_ioctls[nr];
+		func	  = ioctl->func;
+
+		if (!func) {
+			DRM_DEBUG("no function\n");
+			retcode = -EINVAL;
+		} else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN))
+			    || (ioctl->auth_needed && !priv->authenticated)) {
+			retcode = -EACCES;
+		} else {
+			retcode = (func)(inode, filp, cmd, arg);
+		}
+	}
+
+	atomic_dec(&dev->ioctl_count);
+	return retcode;
+}
+
+int radeon_lock(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+        drm_file_t        *priv   = filp->private_data;
+        drm_device_t      *dev    = priv->dev;
+        DECLARE_WAITQUEUE(entry, current);
+        int               ret   = 0;
+        drm_lock_t        lock;
+#if DRM_DMA_HISTOGRAM
+        cycles_t          start;
+
+        dev->lck_start = start = get_cycles();
+#endif
+
+        if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)))
+		return -EFAULT;
+
+        if (lock.context == DRM_KERNEL_CONTEXT) {
+                DRM_ERROR("Process %d using kernel context %d\n",
+                          current->pid, lock.context);
+                return -EINVAL;
+        }
+
+        DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
+                  lock.context, current->pid, dev->lock.hw_lock->lock,
+                  lock.flags);
+
+        if (lock.context < 0 /* || lock.context >= dev->queue_count */)
+                return -EINVAL;
+
+        if (!ret) {
+                add_wait_queue(&dev->lock.lock_queue, &entry);
+                for (;;) {
+                        current->state = TASK_INTERRUPTIBLE;
+                        if (!dev->lock.hw_lock) {
+                                /* Device has been unregistered */
+                                ret = -EINTR;
+                                break;
+                        }
+                        if (drm_lock_take(&dev->lock.hw_lock->lock,
+                                          lock.context)) {
+                                dev->lock.pid       = current->pid;
+                                dev->lock.lock_time = jiffies;
+                                atomic_inc(&dev->total_locks);
+                                break;  /* Got lock */
+                        }
+
+                                /* Contention */
+                        atomic_inc(&dev->total_sleeps);
+                        schedule();
+                        if (signal_pending(current)) {
+                                ret = -ERESTARTSYS;
+                                break;
+                        }
+                }
+                current->state = TASK_RUNNING;
+                remove_wait_queue(&dev->lock.lock_queue, &entry);
+        }
+
+        if (!ret) {
+		sigemptyset(&dev->sigmask);
+		sigaddset(&dev->sigmask, SIGSTOP);
+		sigaddset(&dev->sigmask, SIGTSTP);
+		sigaddset(&dev->sigmask, SIGTTIN);
+		sigaddset(&dev->sigmask, SIGTTOU);
+		dev->sigdata.context = lock.context;
+		dev->sigdata.lock    = dev->lock.hw_lock;
+		block_all_signals(drm_notifier, &dev->sigdata, &dev->sigmask);
+                if (lock.flags & _DRM_LOCK_READY) {
+				/* Wait for space in DMA/FIFO */
+		}
+                if (lock.flags & _DRM_LOCK_QUIESCENT) {
+				/* Make hardware quiescent */
+			DRM_DEBUG("not quiescent!\n");
+#if 0
+                        radeon_quiescent(dev);
+#endif
+		}
+        }
+
+#if LINUX_VERSION_CODE < 0x020400
+	if (lock.context != radeon_res_ctx.handle) {
+		current->counter = 5;
+		current->priority = DEF_PRIORITY/4;
+	}
+#endif
+        DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
+
+#if DRM_DMA_HISTOGRAM
+        atomic_inc(&dev->histo.lacq[drm_histogram_slot(get_cycles() - start)]);
+#endif
+
+        return ret;
+}
+
+
+int radeon_unlock(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	drm_lock_t	  lock;
+
+	if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)))
+		return -EFAULT;
+
+	if (lock.context == DRM_KERNEL_CONTEXT) {
+		DRM_ERROR("Process %d using kernel context %d\n",
+			  current->pid, lock.context);
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("%d frees lock (%d holds)\n",
+		  lock.context,
+		  _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+	atomic_inc(&dev->total_unlocks);
+	if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock))
+		atomic_inc(&dev->total_contends);
+	drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT);
+				/* FIXME: Try to send data to card here */
+	if (!dev->context_flag) {
+		if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				  DRM_KERNEL_CONTEXT)) {
+			DRM_ERROR("\n");
+		}
+	}
+
+#if LINUX_VERSION_CODE < 0x020400
+	if (lock.context != radeon_res_ctx.handle) {
+		current->counter = 5;
+		current->priority = DEF_PRIORITY;
+	}
+#endif
+	unblock_all_signals();
+	return 0;
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/radeon_drv.h linux/drivers/char/drm-4.0/radeon_drv.h
--- linux-2.4.18-pre2/drivers/char/drm-4.0/radeon_drv.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/radeon_drv.h	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,709 @@
+/* radeon_drv.h -- Private header for radeon driver -*- linux-c -*-
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Fremont, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Rickard E. (Rik) Faith <faith@valinux.com>
+ *   Kevin E. Martin <martin@valinux.com>
+ *   Gareth Hughes <gareth@valinux.com>
+ *
+ */
+
+#ifndef __RADEON_DRV_H__
+#define __RADEON_DRV_H__
+
+typedef struct drm_radeon_freelist {
+   	unsigned int age;
+   	drm_buf_t *buf;
+   	struct drm_radeon_freelist *next;
+   	struct drm_radeon_freelist *prev;
+} drm_radeon_freelist_t;
+
+typedef struct drm_radeon_ring_buffer {
+	u32 *start;
+	u32 *end;
+	int size;
+	int size_l2qw;
+
+	volatile u32 *head;
+	u32 tail;
+	u32 tail_mask;
+	int space;
+} drm_radeon_ring_buffer_t;
+
+typedef struct drm_radeon_depth_clear_t {
+	u32 rb3d_cntl;
+	u32 rb3d_zstencilcntl;
+	u32 se_cntl;
+} drm_radeon_depth_clear_t;
+
+typedef struct drm_radeon_private {
+	drm_radeon_ring_buffer_t ring;
+	drm_radeon_sarea_t *sarea_priv;
+
+	int agp_size;
+	u32 agp_vm_start;
+	u32 agp_buffers_offset;
+
+	int cp_mode;
+	int cp_running;
+
+   	drm_radeon_freelist_t *head;
+   	drm_radeon_freelist_t *tail;
+/* FIXME: ROTATE_BUFS is a hask to cycle through bufs until freelist
+   code is used.  Note this hides a problem with the scratch register
+   (used to keep track of last buffer completed) being written to before
+   the last buffer has actually completed rendering. */
+#define ROTATE_BUFS 1
+#if ROTATE_BUFS
+	int last_buf;
+#endif
+	volatile u32 *scratch;
+
+	int usec_timeout;
+	int is_pci;
+
+	atomic_t idle_count;
+
+	int page_flipping;
+	int current_page;
+	u32 crtc_offset;
+	u32 crtc_offset_cntl;
+
+	unsigned int color_fmt;
+	unsigned int front_offset;
+	unsigned int front_pitch;
+	unsigned int back_offset;
+	unsigned int back_pitch;
+
+	unsigned int depth_fmt;
+	unsigned int depth_offset;
+	unsigned int depth_pitch;
+
+	u32 front_pitch_offset;
+	u32 back_pitch_offset;
+	u32 depth_pitch_offset;
+
+	drm_radeon_depth_clear_t depth_clear;
+
+	drm_map_t *sarea;
+	drm_map_t *fb;
+	drm_map_t *mmio;
+	drm_map_t *cp_ring;
+	drm_map_t *ring_rptr;
+	drm_map_t *buffers;
+	drm_map_t *agp_textures;
+} drm_radeon_private_t;
+
+typedef struct drm_radeon_buf_priv {
+	u32 age;
+	int prim;
+	int discard;
+	int dispatched;
+   	drm_radeon_freelist_t *list_entry;
+} drm_radeon_buf_priv_t;
+
+				/* radeon_drv.c */
+extern int  radeon_version( struct inode *inode, struct file *filp,
+			    unsigned int cmd, unsigned long arg );
+extern int  radeon_open( struct inode *inode, struct file *filp );
+extern int  radeon_release( struct inode *inode, struct file *filp );
+extern int  radeon_ioctl( struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg );
+extern int  radeon_lock( struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg );
+extern int  radeon_unlock( struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg );
+
+				/* radeon_cp.c */
+extern int radeon_cp_init( struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg );
+extern int radeon_cp_start( struct inode *inode, struct file *filp,
+			    unsigned int cmd, unsigned long arg );
+extern int radeon_cp_stop( struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg );
+extern int radeon_cp_reset( struct inode *inode, struct file *filp,
+			    unsigned int cmd, unsigned long arg );
+extern int radeon_cp_idle( struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg );
+extern int radeon_engine_reset( struct inode *inode, struct file *filp,
+				unsigned int cmd, unsigned long arg );
+extern int radeon_fullscreen( struct inode *inode, struct file *filp,
+			      unsigned int cmd, unsigned long arg );
+extern int radeon_cp_buffers( struct inode *inode, struct file *filp,
+			      unsigned int cmd, unsigned long arg );
+
+extern void radeon_freelist_reset( drm_device_t *dev );
+extern drm_buf_t *radeon_freelist_get( drm_device_t *dev );
+
+extern int radeon_wait_ring( drm_radeon_private_t *dev_priv, int n );
+extern void radeon_update_ring_snapshot( drm_radeon_private_t *dev_priv );
+
+extern int radeon_do_cp_idle( drm_radeon_private_t *dev_priv );
+extern int radeon_do_cleanup_pageflip( drm_device_t *dev );
+
+				/* radeon_state.c */
+extern int radeon_cp_clear( struct inode *inode, struct file *filp,
+			    unsigned int cmd, unsigned long arg );
+extern int radeon_cp_swap( struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg );
+extern int radeon_cp_vertex( struct inode *inode, struct file *filp,
+			     unsigned int cmd, unsigned long arg );
+extern int radeon_cp_indices( struct inode *inode, struct file *filp,
+			      unsigned int cmd, unsigned long arg );
+extern int radeon_cp_blit( struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg );
+extern int radeon_cp_stipple( struct inode *inode, struct file *filp,
+			      unsigned int cmd, unsigned long arg );
+extern int radeon_cp_indirect( struct inode *inode, struct file *filp,
+			       unsigned int cmd, unsigned long arg );
+
+				/* radeon_bufs.c */
+extern int radeon_addbufs(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int radeon_mapbufs(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+
+				/* radeon_context.c */
+extern int  radeon_resctx(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  radeon_addctx(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  radeon_modctx(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  radeon_getctx(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  radeon_switchctx(struct inode *inode, struct file *filp,
+			     unsigned int cmd, unsigned long arg);
+extern int  radeon_newctx(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  radeon_rmctx(struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg);
+
+extern int  radeon_context_switch(drm_device_t *dev, int old, int new);
+extern int  radeon_context_switch_complete(drm_device_t *dev, int new);
+
+
+/* Register definitions, register access macros and drmAddMap constants
+ * for Radeon kernel driver.
+ */
+
+#define RADEON_AUX_SCISSOR_CNTL		0x26f0
+#	define RADEON_EXCLUSIVE_SCISSOR_0	(1 << 24)
+#	define RADEON_EXCLUSIVE_SCISSOR_1	(1 << 25)
+#	define RADEON_EXCLUSIVE_SCISSOR_2	(1 << 26)
+#	define RADEON_SCISSOR_0_ENABLE		(1 << 28)
+#	define RADEON_SCISSOR_1_ENABLE		(1 << 29)
+#	define RADEON_SCISSOR_2_ENABLE		(1 << 30)
+
+#define RADEON_BUS_CNTL			0x0030
+#	define RADEON_BUS_MASTER_DIS		(1 << 6)
+
+#define RADEON_CLOCK_CNTL_DATA		0x000c
+#	define RADEON_PLL_WR_EN			(1 << 7)
+#define RADEON_CLOCK_CNTL_INDEX		0x0008
+#define RADEON_CONFIG_APER_SIZE		0x0108
+#define RADEON_CRTC_OFFSET		0x0224
+#define RADEON_CRTC_OFFSET_CNTL		0x0228
+#	define RADEON_CRTC_TILE_EN		(1 << 15)
+#	define RADEON_CRTC_OFFSET_FLIP_CNTL	(1 << 16)
+
+#define RADEON_RB3D_COLORPITCH		0x1c48
+#define RADEON_RB3D_DEPTHCLEARVALUE	0x1c30
+#define RADEON_RB3D_DEPTHXY_OFFSET	0x1c60
+
+#define RADEON_DP_GUI_MASTER_CNTL	0x146c
+#	define RADEON_GMC_SRC_PITCH_OFFSET_CNTL	(1 << 0)
+#	define RADEON_GMC_DST_PITCH_OFFSET_CNTL	(1 << 1)
+#	define RADEON_GMC_BRUSH_SOLID_COLOR	(13 << 4)
+#	define RADEON_GMC_BRUSH_NONE		(15 << 4)
+#	define RADEON_GMC_DST_16BPP		(4 << 8)
+#	define RADEON_GMC_DST_24BPP		(5 << 8)
+#	define RADEON_GMC_DST_32BPP		(6 << 8)
+#	define RADEON_GMC_DST_DATATYPE_SHIFT	8
+#	define RADEON_GMC_SRC_DATATYPE_COLOR	(3 << 12)
+#	define RADEON_DP_SRC_SOURCE_MEMORY	(2 << 24)
+#	define RADEON_DP_SRC_SOURCE_HOST_DATA	(3 << 24)
+#	define RADEON_GMC_CLR_CMP_CNTL_DIS	(1 << 28)
+#	define RADEON_GMC_WR_MSK_DIS		(1 << 30)
+#	define RADEON_ROP3_S			0x00cc0000
+#	define RADEON_ROP3_P			0x00f00000
+#define RADEON_DP_WRITE_MASK		0x16cc
+#define RADEON_DST_PITCH_OFFSET		0x142c
+#define RADEON_DST_PITCH_OFFSET_C	0x1c80
+#	define RADEON_DST_TILE_LINEAR		(0 << 30)
+#	define RADEON_DST_TILE_MACRO		(1 << 30)
+#	define RADEON_DST_TILE_MICRO		(2 << 30)
+#	define RADEON_DST_TILE_BOTH		(3 << 30)
+
+#define RADEON_SCRATCH_REG0		0x15e0
+#define RADEON_SCRATCH_REG1		0x15e4
+#define RADEON_SCRATCH_REG2		0x15e8
+#define RADEON_SCRATCH_REG3		0x15ec
+#define RADEON_SCRATCH_REG4		0x15f0
+#define RADEON_SCRATCH_REG5		0x15f4
+#define RADEON_SCRATCH_UMSK		0x0770
+#define RADEON_SCRATCH_ADDR		0x0774
+
+#define RADEON_HOST_PATH_CNTL		0x0130
+#	define RADEON_HDP_SOFT_RESET		(1 << 26)
+#	define RADEON_HDP_WC_TIMEOUT_MASK	(7 << 28)
+#	define RADEON_HDP_WC_TIMEOUT_28BCLK	(7 << 28)
+
+#define RADEON_ISYNC_CNTL		0x1724
+#	define RADEON_ISYNC_ANY2D_IDLE3D	(1 << 0)
+#	define RADEON_ISYNC_ANY3D_IDLE2D	(1 << 1)
+#	define RADEON_ISYNC_TRIG2D_IDLE3D	(1 << 2)
+#	define RADEON_ISYNC_TRIG3D_IDLE2D	(1 << 3)
+#	define RADEON_ISYNC_WAIT_IDLEGUI	(1 << 4)
+#	define RADEON_ISYNC_CPSCRATCH_IDLEGUI	(1 << 5)
+
+#define RADEON_MC_AGP_LOCATION		0x014c
+#define RADEON_MC_FB_LOCATION		0x0148
+#define RADEON_MCLK_CNTL		0x0012
+
+#define RADEON_PP_BORDER_COLOR_0	0x1d40
+#define RADEON_PP_BORDER_COLOR_1	0x1d44
+#define RADEON_PP_BORDER_COLOR_2	0x1d48
+#define RADEON_PP_CNTL			0x1c38
+#	define RADEON_SCISSOR_ENABLE		(1 <<  1)
+#define RADEON_PP_LUM_MATRIX		0x1d00
+#define RADEON_PP_MISC			0x1c14
+#define RADEON_PP_ROT_MATRIX_0		0x1d58
+#define RADEON_PP_TXFILTER_0		0x1c54
+#define RADEON_PP_TXFILTER_1		0x1c6c
+#define RADEON_PP_TXFILTER_2		0x1c84
+
+#define RADEON_RB2D_DSTCACHE_CTLSTAT	0x342c
+#	define RADEON_RB2D_DC_FLUSH		(3 << 0)
+#	define RADEON_RB2D_DC_FREE		(3 << 2)
+#	define RADEON_RB2D_DC_FLUSH_ALL		0xf
+#	define RADEON_RB2D_DC_BUSY		(1 << 31)
+#define RADEON_RB3D_CNTL		0x1c3c
+#	define RADEON_ALPHA_BLEND_ENABLE	(1 << 0)
+#	define RADEON_PLANE_MASK_ENABLE		(1 << 1)
+#	define RADEON_DITHER_ENABLE		(1 << 2)
+#	define RADEON_ROUND_ENABLE		(1 << 3)
+#	define RADEON_SCALE_DITHER_ENABLE	(1 << 4)
+#	define RADEON_DITHER_INIT		(1 << 5)
+#	define RADEON_ROP_ENABLE		(1 << 6)
+#	define RADEON_STENCIL_ENABLE		(1 << 7)
+#	define RADEON_Z_ENABLE			(1 << 8)
+#	define RADEON_DEPTH_XZ_OFFEST_ENABLE	(1 << 9)
+#	define RADEON_ZBLOCK8			(0 << 15)
+#	define RADEON_ZBLOCK16			(1 << 15)
+#define RADEON_RB3D_DEPTHOFFSET		0x1c24
+#define RADEON_RB3D_PLANEMASK		0x1d84
+#define RADEON_RB3D_STENCILREFMASK	0x1d7c
+#define RADEON_RB3D_ZCACHE_MODE		0x3250
+#define RADEON_RB3D_ZCACHE_CTLSTAT	0x3254
+#	define RADEON_RB3D_ZC_FLUSH		(1 << 0)
+#	define RADEON_RB3D_ZC_FREE		(1 << 2)
+#	define RADEON_RB3D_ZC_FLUSH_ALL		0x5
+#	define RADEON_RB3D_ZC_BUSY		(1 << 31)
+#define RADEON_RB3D_ZSTENCILCNTL	0x1c2c
+#	define RADEON_Z_TEST_MASK		(7 << 4)
+#	define RADEON_Z_TEST_ALWAYS		(7 << 4)
+#	define RADEON_STENCIL_TEST_ALWAYS	(7 << 12)
+#	define RADEON_STENCIL_S_FAIL_KEEP	(0 << 16)
+#	define RADEON_STENCIL_ZPASS_KEEP	(0 << 20)
+#	define RADEON_STENCIL_ZFAIL_KEEP	(0 << 20)
+#	define RADEON_Z_WRITE_ENABLE		(1 << 30)
+#define RADEON_RBBM_SOFT_RESET		0x00f0
+#	define RADEON_SOFT_RESET_CP		(1 <<  0)
+#	define RADEON_SOFT_RESET_HI		(1 <<  1)
+#	define RADEON_SOFT_RESET_SE		(1 <<  2)
+#	define RADEON_SOFT_RESET_RE		(1 <<  3)
+#	define RADEON_SOFT_RESET_PP		(1 <<  4)
+#	define RADEON_SOFT_RESET_E2		(1 <<  5)
+#	define RADEON_SOFT_RESET_RB		(1 <<  6)
+#	define RADEON_SOFT_RESET_HDP		(1 <<  7)
+#define RADEON_RBBM_STATUS		0x0e40
+#	define RADEON_RBBM_FIFOCNT_MASK		0x007f
+#	define RADEON_RBBM_ACTIVE		(1 << 31)
+#define RADEON_RE_LINE_PATTERN		0x1cd0
+#define RADEON_RE_MISC			0x26c4
+#define RADEON_RE_TOP_LEFT		0x26c0
+#define RADEON_RE_WIDTH_HEIGHT		0x1c44
+#define RADEON_RE_STIPPLE_ADDR		0x1cc8
+#define RADEON_RE_STIPPLE_DATA		0x1ccc
+
+#define RADEON_SCISSOR_TL_0		0x1cd8
+#define RADEON_SCISSOR_BR_0		0x1cdc
+#define RADEON_SCISSOR_TL_1		0x1ce0
+#define RADEON_SCISSOR_BR_1		0x1ce4
+#define RADEON_SCISSOR_TL_2		0x1ce8
+#define RADEON_SCISSOR_BR_2		0x1cec
+#define RADEON_SE_COORD_FMT		0x1c50
+#define RADEON_SE_CNTL			0x1c4c
+#	define RADEON_FFACE_CULL_CW		(0 << 0)
+#	define RADEON_BFACE_SOLID		(3 << 1)
+#	define RADEON_FFACE_SOLID		(3 << 3)
+#	define RADEON_FLAT_SHADE_VTX_LAST	(3 << 6)
+#	define RADEON_DIFFUSE_SHADE_FLAT	(1 << 8)
+#	define RADEON_DIFFUSE_SHADE_GOURAUD	(2 << 8)
+#	define RADEON_ALPHA_SHADE_FLAT		(1 << 10)
+#	define RADEON_ALPHA_SHADE_GOURAUD	(2 << 10)
+#	define RADEON_SPECULAR_SHADE_FLAT	(1 << 12)
+#	define RADEON_SPECULAR_SHADE_GOURAUD	(2 << 12)
+#	define RADEON_FOG_SHADE_FLAT		(1 << 14)
+#	define RADEON_FOG_SHADE_GOURAUD		(2 << 14)
+#	define RADEON_VPORT_XY_XFORM_ENABLE	(1 << 24)
+#	define RADEON_VPORT_Z_XFORM_ENABLE	(1 << 25)
+#	define RADEON_VTX_PIX_CENTER_OGL	(1 << 27)
+#	define RADEON_ROUND_MODE_TRUNC		(0 << 28)
+#	define RADEON_ROUND_PREC_8TH_PIX	(1 << 30)
+#define RADEON_SE_CNTL_STATUS		0x2140
+#define RADEON_SE_LINE_WIDTH		0x1db8
+#define RADEON_SE_VPORT_XSCALE		0x1d98
+#define RADEON_SURFACE_ACCESS_FLAGS	0x0bf8
+#define RADEON_SURFACE_ACCESS_CLR	0x0bfc
+#define RADEON_SURFACE_CNTL		0x0b00
+#	define RADEON_SURF_TRANSLATION_DIS	(1 << 8)
+#	define RADEON_NONSURF_AP0_SWP_MASK	(3 << 20)
+#	define RADEON_NONSURF_AP0_SWP_LITTLE	(0 << 20)
+#	define RADEON_NONSURF_AP0_SWP_BIG16	(1 << 20)
+#	define RADEON_NONSURF_AP0_SWP_BIG32	(2 << 20)
+#	define RADEON_NONSURF_AP1_SWP_MASK	(3 << 22)
+#	define RADEON_NONSURF_AP1_SWP_LITTLE	(0 << 22)
+#	define RADEON_NONSURF_AP1_SWP_BIG16	(1 << 22)
+#	define RADEON_NONSURF_AP1_SWP_BIG32	(2 << 22)
+#define RADEON_SURFACE0_INFO		0x0b0c
+#	define RADEON_SURF_PITCHSEL_MASK	(0x1ff << 0)
+#	define RADEON_SURF_TILE_MODE_MASK	(3 << 16)
+#	define RADEON_SURF_TILE_MODE_MACRO	(0 << 16)
+#	define RADEON_SURF_TILE_MODE_MICRO	(1 << 16)
+#	define RADEON_SURF_TILE_MODE_32BIT_Z	(2 << 16)
+#	define RADEON_SURF_TILE_MODE_16BIT_Z	(3 << 16)
+#define RADEON_SURFACE0_LOWER_BOUND	0x0b04
+#define RADEON_SURFACE0_UPPER_BOUND	0x0b08
+#define RADEON_SURFACE1_INFO		0x0b1c
+#define RADEON_SURFACE1_LOWER_BOUND	0x0b14
+#define RADEON_SURFACE1_UPPER_BOUND	0x0b18
+#define RADEON_SURFACE2_INFO		0x0b2c
+#define RADEON_SURFACE2_LOWER_BOUND	0x0b24
+#define RADEON_SURFACE2_UPPER_BOUND	0x0b28
+#define RADEON_SURFACE3_INFO		0x0b3c
+#define RADEON_SURFACE3_LOWER_BOUND	0x0b34
+#define RADEON_SURFACE3_UPPER_BOUND	0x0b38
+#define RADEON_SURFACE4_INFO		0x0b4c
+#define RADEON_SURFACE4_LOWER_BOUND	0x0b44
+#define RADEON_SURFACE4_UPPER_BOUND	0x0b48
+#define RADEON_SURFACE5_INFO		0x0b5c
+#define RADEON_SURFACE5_LOWER_BOUND	0x0b54
+#define RADEON_SURFACE5_UPPER_BOUND	0x0b58
+#define RADEON_SURFACE6_INFO		0x0b6c
+#define RADEON_SURFACE6_LOWER_BOUND	0x0b64
+#define RADEON_SURFACE6_UPPER_BOUND	0x0b68
+#define RADEON_SURFACE7_INFO		0x0b7c
+#define RADEON_SURFACE7_LOWER_BOUND	0x0b74
+#define RADEON_SURFACE7_UPPER_BOUND	0x0b78
+#define RADEON_SW_SEMAPHORE		0x013c
+
+#define RADEON_WAIT_UNTIL		0x1720
+#	define RADEON_WAIT_CRTC_PFLIP		(1 << 0)
+#	define RADEON_WAIT_2D_IDLECLEAN		(1 << 16)
+#	define RADEON_WAIT_3D_IDLECLEAN		(1 << 17)
+#	define RADEON_WAIT_HOST_IDLECLEAN	(1 << 18)
+
+#define RADEON_RB3D_ZMASKOFFSET		0x1c34
+#define RADEON_RB3D_ZSTENCILCNTL	0x1c2c
+#	define RADEON_DEPTH_FORMAT_16BIT_INT_Z	(0 << 0)
+#	define RADEON_DEPTH_FORMAT_24BIT_INT_Z	(2 << 0)
+
+
+/* CP registers */
+#define RADEON_CP_ME_RAM_ADDR		0x07d4
+#define RADEON_CP_ME_RAM_RADDR		0x07d8
+#define RADEON_CP_ME_RAM_DATAH		0x07dc
+#define RADEON_CP_ME_RAM_DATAL		0x07e0
+
+#define RADEON_CP_RB_BASE		0x0700
+#define RADEON_CP_RB_CNTL		0x0704
+#define RADEON_CP_RB_RPTR_ADDR		0x070c
+#define RADEON_CP_RB_RPTR		0x0710
+#define RADEON_CP_RB_WPTR		0x0714
+
+#define RADEON_CP_RB_WPTR_DELAY		0x0718
+#	define RADEON_PRE_WRITE_TIMER_SHIFT	0
+#	define RADEON_PRE_WRITE_LIMIT_SHIFT	23
+
+#define RADEON_CP_IB_BASE		0x0738
+
+#define RADEON_CP_CSQ_CNTL		0x0740
+#	define RADEON_CSQ_CNT_PRIMARY_MASK	(0xff << 0)
+#	define RADEON_CSQ_PRIDIS_INDDIS		(0 << 28)
+#	define RADEON_CSQ_PRIPIO_INDDIS		(1 << 28)
+#	define RADEON_CSQ_PRIBM_INDDIS		(2 << 28)
+#	define RADEON_CSQ_PRIPIO_INDBM		(3 << 28)
+#	define RADEON_CSQ_PRIBM_INDBM		(4 << 28)
+#	define RADEON_CSQ_PRIPIO_INDPIO		(15 << 28)
+
+#define RADEON_AIC_CNTL			0x01d0
+#	define RADEON_PCIGART_TRANSLATE_EN	(1 << 0)
+
+/* CP command packets */
+#define RADEON_CP_PACKET0		0x00000000
+#	define RADEON_ONE_REG_WR		(1 << 15)
+#define RADEON_CP_PACKET1		0x40000000
+#define RADEON_CP_PACKET2		0x80000000
+#define RADEON_CP_PACKET3		0xC0000000
+#	define RADEON_3D_RNDR_GEN_INDX_PRIM	0x00002300
+#	define RADEON_WAIT_FOR_IDLE		0x00002600
+#	define RADEON_3D_DRAW_IMMD		0x00002900
+#	define RADEON_3D_CLEAR_ZMASK		0x00003200
+#	define RADEON_CNTL_HOSTDATA_BLT		0x00009400
+#	define RADEON_CNTL_PAINT_MULTI		0x00009A00
+#	define RADEON_CNTL_BITBLT_MULTI		0x00009B00
+
+#define RADEON_CP_PACKET_MASK		0xC0000000
+#define RADEON_CP_PACKET_COUNT_MASK	0x3fff0000
+#define RADEON_CP_PACKET0_REG_MASK	0x000007ff
+#define RADEON_CP_PACKET1_REG0_MASK	0x000007ff
+#define RADEON_CP_PACKET1_REG1_MASK	0x003ff800
+
+#define RADEON_VTX_Z_PRESENT			(1 << 31)
+
+#define RADEON_PRIM_TYPE_NONE			(0 << 0)
+#define RADEON_PRIM_TYPE_POINT			(1 << 0)
+#define RADEON_PRIM_TYPE_LINE			(2 << 0)
+#define RADEON_PRIM_TYPE_LINE_STRIP		(3 << 0)
+#define RADEON_PRIM_TYPE_TRI_LIST		(4 << 0)
+#define RADEON_PRIM_TYPE_TRI_FAN		(5 << 0)
+#define RADEON_PRIM_TYPE_TRI_STRIP		(6 << 0)
+#define RADEON_PRIM_TYPE_TRI_TYPE2		(7 << 0)
+#define RADEON_PRIM_TYPE_RECT_LIST		(8 << 0)
+#define RADEON_PRIM_TYPE_3VRT_POINT_LIST	(9 << 0)
+#define RADEON_PRIM_TYPE_3VRT_LINE_LIST		(10 << 0)
+#define RADEON_PRIM_WALK_IND			(1 << 4)
+#define RADEON_PRIM_WALK_LIST			(2 << 4)
+#define RADEON_PRIM_WALK_RING			(3 << 4)
+#define RADEON_COLOR_ORDER_BGRA			(0 << 6)
+#define RADEON_COLOR_ORDER_RGBA			(1 << 6)
+#define RADEON_MAOS_ENABLE			(1 << 7)
+#define RADEON_VTX_FMT_R128_MODE		(0 << 8)
+#define RADEON_VTX_FMT_RADEON_MODE		(1 << 8)
+#define RADEON_NUM_VERTICES_SHIFT		16
+
+#define RADEON_COLOR_FORMAT_CI8		2
+#define RADEON_COLOR_FORMAT_ARGB1555	3
+#define RADEON_COLOR_FORMAT_RGB565	4
+#define RADEON_COLOR_FORMAT_ARGB8888	6
+#define RADEON_COLOR_FORMAT_RGB332	7
+#define RADEON_COLOR_FORMAT_RGB8	9
+#define RADEON_COLOR_FORMAT_ARGB4444	15
+
+#define RADEON_TXF_8BPP_I		0
+#define RADEON_TXF_16BPP_AI88		1
+#define RADEON_TXF_8BPP_RGB332		2
+#define RADEON_TXF_16BPP_ARGB1555	3
+#define RADEON_TXF_16BPP_RGB565		4
+#define RADEON_TXF_16BPP_ARGB4444	5
+#define RADEON_TXF_32BPP_ARGB8888	6
+#define RADEON_TXF_32BPP_RGBA8888	7
+
+/* Constants */
+#define RADEON_MAX_USEC_TIMEOUT		100000	/* 100 ms */
+
+#define RADEON_LAST_FRAME_REG		RADEON_SCRATCH_REG0
+#define RADEON_LAST_DISPATCH_REG	RADEON_SCRATCH_REG1
+#define RADEON_LAST_CLEAR_REG		RADEON_SCRATCH_REG2
+#define RADEON_LAST_DISPATCH		1
+
+#define RADEON_MAX_VB_AGE		0x7fffffff
+#define RADEON_MAX_VB_VERTS		(0xffff)
+
+
+#define RADEON_BASE(reg)	((u32)(dev_priv->mmio->handle))
+#define RADEON_ADDR(reg)	(RADEON_BASE(reg) + reg)
+
+#define RADEON_DEREF(reg)	*(__volatile__ u32 *)RADEON_ADDR(reg)
+#define RADEON_READ(reg)	RADEON_DEREF(reg)
+#define RADEON_WRITE(reg,val)	do { RADEON_DEREF(reg) = val; } while (0)
+
+#define RADEON_DEREF8(reg)	*(__volatile__ u8 *)RADEON_ADDR(reg)
+#define RADEON_READ8(reg)	RADEON_DEREF8(reg)
+#define RADEON_WRITE8(reg,val)	do { RADEON_DEREF8(reg) = val; } while (0)
+
+#define RADEON_WRITE_PLL(addr,val)                                            \
+do {                                                                          \
+	RADEON_WRITE8(RADEON_CLOCK_CNTL_INDEX,                                \
+		      ((addr) & 0x1f) | RADEON_PLL_WR_EN);                    \
+	RADEON_WRITE(RADEON_CLOCK_CNTL_DATA, (val));                          \
+} while (0)
+
+extern int RADEON_READ_PLL(drm_device_t *dev, int addr);
+
+
+
+#define CP_PACKET0( reg, n )						\
+	(RADEON_CP_PACKET0 | ((n) << 16) | ((reg) >> 2))
+#define CP_PACKET0_TABLE( reg, n )					\
+	(RADEON_CP_PACKET0 | RADEON_ONE_REG_WR | ((n) << 16) | ((reg) >> 2))
+#define CP_PACKET1( reg0, reg1 )					\
+	(RADEON_CP_PACKET1 | (((reg1) >> 2) << 15) | ((reg0) >> 2))
+#define CP_PACKET2()							\
+	(RADEON_CP_PACKET2)
+#define CP_PACKET3( pkt, n )						\
+	(RADEON_CP_PACKET3 | (pkt) | ((n) << 16))
+
+
+/* ================================================================
+ * Engine control helper macros
+ */
+
+#define RADEON_WAIT_UNTIL_2D_IDLE()					\
+do {									\
+	OUT_RING( CP_PACKET0( RADEON_WAIT_UNTIL, 0 ) );			\
+	OUT_RING( (RADEON_WAIT_2D_IDLECLEAN |				\
+		   RADEON_WAIT_HOST_IDLECLEAN) );			\
+} while (0)
+
+#define RADEON_WAIT_UNTIL_3D_IDLE()					\
+do {									\
+	OUT_RING( CP_PACKET0( RADEON_WAIT_UNTIL, 0 ) );			\
+	OUT_RING( (RADEON_WAIT_3D_IDLECLEAN |				\
+		   RADEON_WAIT_HOST_IDLECLEAN) );			\
+} while (0)
+
+#define RADEON_WAIT_UNTIL_IDLE()					\
+do {									\
+	OUT_RING( CP_PACKET0( RADEON_WAIT_UNTIL, 0 ) );			\
+	OUT_RING( (RADEON_WAIT_2D_IDLECLEAN |				\
+		   RADEON_WAIT_3D_IDLECLEAN |				\
+		   RADEON_WAIT_HOST_IDLECLEAN) );			\
+} while (0)
+
+#define RADEON_WAIT_UNTIL_PAGE_FLIPPED()				\
+do {									\
+	OUT_RING( CP_PACKET0( RADEON_WAIT_UNTIL, 0 ) );			\
+	OUT_RING( RADEON_WAIT_CRTC_PFLIP );				\
+} while (0)
+
+#define RADEON_FLUSH_CACHE()						\
+do {									\
+	OUT_RING( CP_PACKET0( RADEON_RB2D_DSTCACHE_CTLSTAT, 0 ) );	\
+	OUT_RING( RADEON_RB2D_DC_FLUSH );				\
+} while (0)
+
+#define RADEON_PURGE_CACHE()						\
+do {									\
+	OUT_RING( CP_PACKET0( RADEON_RB2D_DSTCACHE_CTLSTAT, 0 ) );	\
+	OUT_RING( RADEON_RB2D_DC_FLUSH_ALL );				\
+} while (0)
+
+#define RADEON_FLUSH_ZCACHE()						\
+do {									\
+	OUT_RING( CP_PACKET0( RADEON_RB3D_ZCACHE_CTLSTAT, 0 ) );	\
+	OUT_RING( RADEON_RB3D_ZC_FLUSH );				\
+} while (0)
+
+#define RADEON_PURGE_ZCACHE()						\
+do {									\
+	OUT_RING( CP_PACKET0( RADEON_RB3D_ZCACHE_CTLSTAT, 0 ) );	\
+	OUT_RING( RADEON_RB3D_ZC_FLUSH_ALL );				\
+} while (0)
+
+
+/* ================================================================
+ * Misc helper macros
+ */
+
+#define VB_AGE_CHECK_WITH_RET( dev_priv )				\
+do {									\
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;		\
+	if ( sarea_priv->last_dispatch >= RADEON_MAX_VB_AGE ) {		\
+		int __ret = radeon_do_cp_idle( dev_priv );		\
+		if ( __ret < 0 ) return __ret;				\
+		sarea_priv->last_dispatch = 0;				\
+		radeon_freelist_reset( dev );				\
+	}								\
+} while (0)
+
+#define RADEON_DISPATCH_AGE( age )					\
+do {									\
+	OUT_RING( CP_PACKET0( RADEON_LAST_DISPATCH_REG, 0 ) );		\
+	OUT_RING( age );						\
+} while (0)
+
+#define RADEON_FRAME_AGE( age )						\
+do {									\
+	OUT_RING( CP_PACKET0( RADEON_LAST_FRAME_REG, 0 ) );		\
+	OUT_RING( age );						\
+} while (0)
+
+#define RADEON_CLEAR_AGE( age )						\
+do {									\
+	OUT_RING( CP_PACKET0( RADEON_LAST_CLEAR_REG, 0 ) );		\
+	OUT_RING( age );						\
+} while (0)
+
+
+/* ================================================================
+ * Ring control
+ */
+
+#define radeon_flush_write_combine()	mb()
+
+
+#define RADEON_VERBOSE	0
+
+#define RING_LOCALS	int write; unsigned int mask; volatile u32 *ring;
+
+#define BEGIN_RING( n ) do {						\
+	if ( RADEON_VERBOSE ) {						\
+		DRM_INFO( "BEGIN_RING( %d ) in %s\n",			\
+			   n, __FUNCTION__ );				\
+	}								\
+	if ( dev_priv->ring.space < (n) * sizeof(u32) ) {		\
+		radeon_wait_ring( dev_priv, (n) * sizeof(u32) );	\
+	}								\
+	dev_priv->ring.space -= (n) * sizeof(u32);			\
+	ring = dev_priv->ring.start;					\
+	write = dev_priv->ring.tail;					\
+	mask = dev_priv->ring.tail_mask;				\
+} while (0)
+
+#define ADVANCE_RING() do {						\
+	if ( RADEON_VERBOSE ) {						\
+		DRM_INFO( "ADVANCE_RING() tail=0x%06x wr=0x%06x\n",	\
+			  write, dev_priv->ring.tail );			\
+	}								\
+	radeon_flush_write_combine();					\
+	dev_priv->ring.tail = write;					\
+	RADEON_WRITE( RADEON_CP_RB_WPTR, write );			\
+} while (0)
+
+#define OUT_RING( x ) do {						\
+	if ( RADEON_VERBOSE ) {						\
+		DRM_INFO( "   OUT_RING( 0x%08x ) at 0x%x\n",		\
+			   (unsigned int)(x), write );			\
+	}								\
+	ring[write++] = (x);						\
+	write &= mask;							\
+} while (0)
+
+#define RADEON_PERFORMANCE_BOXES	0
+
+#endif /* __RADEON_DRV_H__ */
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/radeon_state.c linux/drivers/char/drm-4.0/radeon_state.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/radeon_state.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/radeon_state.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,1447 @@
+/* radeon_state.c -- State support for Radeon -*- linux-c -*-
+ *
+ * Copyright 2000 VA Linux Systems, Inc., Fremont, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Kevin E. Martin <martin@valinux.com>
+ *    Gareth Hughes <gareth@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "radeon_drv.h"
+#include "drm.h"
+#include <linux/delay.h>
+
+
+/* ================================================================
+ * CP hardware state programming functions
+ */
+
+static inline void radeon_emit_clip_rect( drm_radeon_private_t *dev_priv,
+					  drm_clip_rect_t *box )
+{
+	RING_LOCALS;
+
+	DRM_DEBUG( "   box:  x1=%d y1=%d  x2=%d y2=%d\n",
+		   box->x1, box->y1, box->x2, box->y2 );
+
+	BEGIN_RING( 4 );
+
+	OUT_RING( CP_PACKET0( RADEON_RE_TOP_LEFT, 0 ) );
+	OUT_RING( (box->y1 << 16) | box->x1 );
+
+	OUT_RING( CP_PACKET0( RADEON_RE_WIDTH_HEIGHT, 0 ) );
+	OUT_RING( ((box->y2 - 1) << 16) | (box->x2 - 1) );
+
+	ADVANCE_RING();
+}
+
+static inline void radeon_emit_context( drm_radeon_private_t *dev_priv )
+{
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_radeon_context_regs_t *ctx = &sarea_priv->context_state;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 14 );
+
+	OUT_RING( CP_PACKET0( RADEON_PP_MISC, 6 ) );
+	OUT_RING( ctx->pp_misc );
+	OUT_RING( ctx->pp_fog_color );
+	OUT_RING( ctx->re_solid_color );
+	OUT_RING( ctx->rb3d_blendcntl );
+	OUT_RING( ctx->rb3d_depthoffset );
+	OUT_RING( ctx->rb3d_depthpitch );
+	OUT_RING( ctx->rb3d_zstencilcntl );
+
+	OUT_RING( CP_PACKET0( RADEON_PP_CNTL, 2 ) );
+	OUT_RING( ctx->pp_cntl );
+	OUT_RING( ctx->rb3d_cntl );
+	OUT_RING( ctx->rb3d_coloroffset );
+
+	OUT_RING( CP_PACKET0( RADEON_RB3D_COLORPITCH, 0 ) );
+	OUT_RING( ctx->rb3d_colorpitch );
+
+	ADVANCE_RING();
+}
+
+static inline void radeon_emit_vertfmt( drm_radeon_private_t *dev_priv )
+{
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_radeon_context_regs_t *ctx = &sarea_priv->context_state;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 2 );
+
+	OUT_RING( CP_PACKET0( RADEON_SE_COORD_FMT, 0 ) );
+	OUT_RING( ctx->se_coord_fmt );
+
+	ADVANCE_RING();
+}
+
+static inline void radeon_emit_line( drm_radeon_private_t *dev_priv )
+{
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_radeon_context_regs_t *ctx = &sarea_priv->context_state;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 5 );
+
+	OUT_RING( CP_PACKET0( RADEON_RE_LINE_PATTERN, 1 ) );
+	OUT_RING( ctx->re_line_pattern );
+	OUT_RING( ctx->re_line_state );
+
+	OUT_RING( CP_PACKET0( RADEON_SE_LINE_WIDTH, 0 ) );
+	OUT_RING( ctx->se_line_width );
+
+	ADVANCE_RING();
+}
+
+static inline void radeon_emit_bumpmap( drm_radeon_private_t *dev_priv )
+{
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_radeon_context_regs_t *ctx = &sarea_priv->context_state;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 5 );
+
+	OUT_RING( CP_PACKET0( RADEON_PP_LUM_MATRIX, 0 ) );
+	OUT_RING( ctx->pp_lum_matrix );
+
+	OUT_RING( CP_PACKET0( RADEON_PP_ROT_MATRIX_0, 1 ) );
+	OUT_RING( ctx->pp_rot_matrix_0 );
+	OUT_RING( ctx->pp_rot_matrix_1 );
+
+	ADVANCE_RING();
+}
+
+static inline void radeon_emit_masks( drm_radeon_private_t *dev_priv )
+{
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_radeon_context_regs_t *ctx = &sarea_priv->context_state;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 4 );
+
+	OUT_RING( CP_PACKET0( RADEON_RB3D_STENCILREFMASK, 2 ) );
+	OUT_RING( ctx->rb3d_stencilrefmask );
+	OUT_RING( ctx->rb3d_ropcntl );
+	OUT_RING( ctx->rb3d_planemask );
+
+	ADVANCE_RING();
+}
+
+static inline void radeon_emit_viewport( drm_radeon_private_t *dev_priv )
+{
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_radeon_context_regs_t *ctx = &sarea_priv->context_state;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 7 );
+
+	OUT_RING( CP_PACKET0( RADEON_SE_VPORT_XSCALE, 5 ) );
+	OUT_RING( ctx->se_vport_xscale );
+	OUT_RING( ctx->se_vport_xoffset );
+	OUT_RING( ctx->se_vport_yscale );
+	OUT_RING( ctx->se_vport_yoffset );
+	OUT_RING( ctx->se_vport_zscale );
+	OUT_RING( ctx->se_vport_zoffset );
+
+	ADVANCE_RING();
+}
+
+static inline void radeon_emit_setup( drm_radeon_private_t *dev_priv )
+{
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_radeon_context_regs_t *ctx = &sarea_priv->context_state;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 4 );
+
+	OUT_RING( CP_PACKET0( RADEON_SE_CNTL, 0 ) );
+	OUT_RING( ctx->se_cntl );
+	OUT_RING( CP_PACKET0( RADEON_SE_CNTL_STATUS, 0 ) );
+	OUT_RING( ctx->se_cntl_status );
+
+	ADVANCE_RING();
+}
+
+static inline void radeon_emit_tcl( drm_radeon_private_t *dev_priv )
+{
+#ifdef TCL_ENABLE
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_radeon_context_regs_t *ctx = &sarea_priv->context_state;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 29 );
+
+	OUT_RING( CP_PACKET0( RADEON_SE_TCL_MATERIAL_EMMISSIVE_RED, 27 ) );
+	OUT_RING( ctx->se_tcl_material_emmissive.red );
+	OUT_RING( ctx->se_tcl_material_emmissive.green );
+	OUT_RING( ctx->se_tcl_material_emmissive.blue );
+	OUT_RING( ctx->se_tcl_material_emmissive.alpha );
+	OUT_RING( ctx->se_tcl_material_ambient.red );
+	OUT_RING( ctx->se_tcl_material_ambient.green );
+	OUT_RING( ctx->se_tcl_material_ambient.blue );
+	OUT_RING( ctx->se_tcl_material_ambient.alpha );
+	OUT_RING( ctx->se_tcl_material_diffuse.red );
+	OUT_RING( ctx->se_tcl_material_diffuse.green );
+	OUT_RING( ctx->se_tcl_material_diffuse.blue );
+	OUT_RING( ctx->se_tcl_material_diffuse.alpha );
+	OUT_RING( ctx->se_tcl_material_specular.red );
+	OUT_RING( ctx->se_tcl_material_specular.green );
+	OUT_RING( ctx->se_tcl_material_specular.blue );
+	OUT_RING( ctx->se_tcl_material_specular.alpha );
+	OUT_RING( ctx->se_tcl_shininess );
+	OUT_RING( ctx->se_tcl_output_vtx_fmt );
+	OUT_RING( ctx->se_tcl_output_vtx_sel );
+	OUT_RING( ctx->se_tcl_matrix_select_0 );
+	OUT_RING( ctx->se_tcl_matrix_select_1 );
+	OUT_RING( ctx->se_tcl_ucp_vert_blend_ctl );
+	OUT_RING( ctx->se_tcl_texture_proc_ctl );
+	OUT_RING( ctx->se_tcl_light_model_ctl );
+	for ( i = 0 ; i < 4 ; i++ ) {
+		OUT_RING( ctx->se_tcl_per_light_ctl[i] );
+	}
+
+	ADVANCE_RING();
+#else
+	DRM_ERROR( "TCL not enabled!\n" );
+#endif
+}
+
+static inline void radeon_emit_misc( drm_radeon_private_t *dev_priv )
+{
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_radeon_context_regs_t *ctx = &sarea_priv->context_state;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 2 );
+
+	OUT_RING( CP_PACKET0( RADEON_RE_MISC, 0 ) );
+	OUT_RING( ctx->re_misc );
+
+	ADVANCE_RING();
+}
+
+static inline void radeon_emit_tex0( drm_radeon_private_t *dev_priv )
+{
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_radeon_texture_regs_t *tex = &sarea_priv->tex_state[0];
+	RING_LOCALS;
+	DRM_DEBUG( "    %s: offset=0x%x\n", __FUNCTION__, tex->pp_txoffset );
+
+	BEGIN_RING( 9 );
+
+	OUT_RING( CP_PACKET0( RADEON_PP_TXFILTER_0, 5 ) );
+	OUT_RING( tex->pp_txfilter );
+	OUT_RING( tex->pp_txformat );
+	OUT_RING( tex->pp_txoffset );
+	OUT_RING( tex->pp_txcblend );
+	OUT_RING( tex->pp_txablend );
+	OUT_RING( tex->pp_tfactor );
+
+	OUT_RING( CP_PACKET0( RADEON_PP_BORDER_COLOR_0, 0 ) );
+	OUT_RING( tex->pp_border_color );
+
+	ADVANCE_RING();
+}
+
+static inline void radeon_emit_tex1( drm_radeon_private_t *dev_priv )
+{
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_radeon_texture_regs_t *tex = &sarea_priv->tex_state[1];
+	RING_LOCALS;
+	DRM_DEBUG( "    %s: offset=0x%x\n", __FUNCTION__, tex->pp_txoffset );
+
+	BEGIN_RING( 9 );
+
+	OUT_RING( CP_PACKET0( RADEON_PP_TXFILTER_1, 5 ) );
+	OUT_RING( tex->pp_txfilter );
+	OUT_RING( tex->pp_txformat );
+	OUT_RING( tex->pp_txoffset );
+	OUT_RING( tex->pp_txcblend );
+	OUT_RING( tex->pp_txablend );
+	OUT_RING( tex->pp_tfactor );
+
+	OUT_RING( CP_PACKET0( RADEON_PP_BORDER_COLOR_1, 0 ) );
+	OUT_RING( tex->pp_border_color );
+
+	ADVANCE_RING();
+}
+
+static inline void radeon_emit_tex2( drm_radeon_private_t *dev_priv )
+{
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_radeon_texture_regs_t *tex = &sarea_priv->tex_state[2];
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 9 );
+
+	OUT_RING( CP_PACKET0( RADEON_PP_TXFILTER_2, 5 ) );
+	OUT_RING( tex->pp_txfilter );
+	OUT_RING( tex->pp_txformat );
+	OUT_RING( tex->pp_txoffset );
+	OUT_RING( tex->pp_txcblend );
+	OUT_RING( tex->pp_txablend );
+	OUT_RING( tex->pp_tfactor );
+
+	OUT_RING( CP_PACKET0( RADEON_PP_BORDER_COLOR_2, 0 ) );
+	OUT_RING( tex->pp_border_color );
+
+	ADVANCE_RING();
+}
+
+static inline void radeon_emit_state( drm_radeon_private_t *dev_priv )
+{
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int dirty = sarea_priv->dirty;
+
+	DRM_DEBUG( "%s: dirty=0x%08x\n", __FUNCTION__, dirty );
+
+	if ( dirty & RADEON_UPLOAD_CONTEXT ) {
+		radeon_emit_context( dev_priv );
+		sarea_priv->dirty &= ~RADEON_UPLOAD_CONTEXT;
+	}
+
+	if ( dirty & RADEON_UPLOAD_VERTFMT ) {
+		radeon_emit_vertfmt( dev_priv );
+		sarea_priv->dirty &= ~RADEON_UPLOAD_VERTFMT;
+	}
+
+	if ( dirty & RADEON_UPLOAD_LINE ) {
+		radeon_emit_line( dev_priv );
+		sarea_priv->dirty &= ~RADEON_UPLOAD_LINE;
+	}
+
+	if ( dirty & RADEON_UPLOAD_BUMPMAP ) {
+		radeon_emit_bumpmap( dev_priv );
+		sarea_priv->dirty &= ~RADEON_UPLOAD_BUMPMAP;
+	}
+
+	if ( dirty & RADEON_UPLOAD_MASKS ) {
+		radeon_emit_masks( dev_priv );
+		sarea_priv->dirty &= ~RADEON_UPLOAD_MASKS;
+	}
+
+	if ( dirty & RADEON_UPLOAD_VIEWPORT ) {
+		radeon_emit_viewport( dev_priv );
+		sarea_priv->dirty &= ~RADEON_UPLOAD_VIEWPORT;
+	}
+
+	if ( dirty & RADEON_UPLOAD_SETUP ) {
+		radeon_emit_setup( dev_priv );
+		sarea_priv->dirty &= ~RADEON_UPLOAD_SETUP;
+	}
+
+	if ( dirty & RADEON_UPLOAD_TCL ) {
+#ifdef TCL_ENABLE
+		radeon_emit_tcl( dev_priv );
+#endif
+		sarea_priv->dirty &= ~RADEON_UPLOAD_TCL;
+	}
+
+	if ( dirty & RADEON_UPLOAD_MISC ) {
+		radeon_emit_misc( dev_priv );
+		sarea_priv->dirty &= ~RADEON_UPLOAD_MISC;
+	}
+
+	if ( dirty & RADEON_UPLOAD_TEX0 ) {
+		radeon_emit_tex0( dev_priv );
+		sarea_priv->dirty &= ~RADEON_UPLOAD_TEX0;
+	}
+
+	if ( dirty & RADEON_UPLOAD_TEX1 ) {
+		radeon_emit_tex1( dev_priv );
+		sarea_priv->dirty &= ~RADEON_UPLOAD_TEX1;
+	}
+
+	if ( dirty & RADEON_UPLOAD_TEX2 ) {
+#if 0
+		radeon_emit_tex2( dev_priv );
+#endif
+		sarea_priv->dirty &= ~RADEON_UPLOAD_TEX2;
+	}
+
+	sarea_priv->dirty &= ~(RADEON_UPLOAD_TEX0IMAGES |
+			       RADEON_UPLOAD_TEX1IMAGES |
+			       RADEON_UPLOAD_TEX2IMAGES |
+			       RADEON_REQUIRE_QUIESCENCE);
+}
+
+
+#if RADEON_PERFORMANCE_BOXES
+/* ================================================================
+ * Performance monitoring functions
+ */
+
+static void radeon_clear_box( drm_radeon_private_t *dev_priv,
+			      int x, int y, int w, int h,
+			      int r, int g, int b )
+{
+	u32 pitch, offset;
+	u32 color;
+	RING_LOCALS;
+
+	switch ( dev_priv->color_fmt ) {
+	case RADEON_COLOR_FORMAT_RGB565:
+		color = (((r & 0xf8) << 8) |
+			 ((g & 0xfc) << 3) |
+			 ((b & 0xf8) >> 3));
+		break;
+	case RADEON_COLOR_FORMAT_ARGB8888:
+	default:
+		color = (((0xff) << 24) | (r << 16) | (g <<  8) | b);
+		break;
+	}
+
+	offset = dev_priv->back_offset;
+	pitch = dev_priv->back_pitch >> 3;
+
+	BEGIN_RING( 6 );
+
+	OUT_RING( CP_PACKET3( RADEON_CNTL_PAINT_MULTI, 4 ) );
+	OUT_RING( RADEON_GMC_DST_PITCH_OFFSET_CNTL |
+		  RADEON_GMC_BRUSH_SOLID_COLOR |
+		  (dev_priv->color_fmt << 8) |
+		  RADEON_GMC_SRC_DATATYPE_COLOR |
+		  RADEON_ROP3_P |
+		  RADEON_GMC_CLR_CMP_CNTL_DIS );
+
+	OUT_RING( (pitch << 22) | (offset >> 5) );
+	OUT_RING( color );
+
+	OUT_RING( (x << 16) | y );
+	OUT_RING( (w << 16) | h );
+
+	ADVANCE_RING();
+}
+
+static void radeon_cp_performance_boxes( drm_radeon_private_t *dev_priv )
+{
+	if ( atomic_read( &dev_priv->idle_count ) == 0 ) {
+		radeon_clear_box( dev_priv, 64, 4, 8, 8, 0, 255, 0 );
+	} else {
+		atomic_set( &dev_priv->idle_count, 0 );
+	}
+}
+
+#endif
+
+
+/* ================================================================
+ * CP command dispatch functions
+ */
+
+static void radeon_print_dirty( const char *msg, unsigned int flags )
+{
+	DRM_DEBUG( "%s: (0x%x) %s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
+		   msg,
+		   flags,
+		   (flags & RADEON_UPLOAD_CONTEXT)     ? "context, " : "",
+		   (flags & RADEON_UPLOAD_VERTFMT)     ? "vertfmt, " : "",
+		   (flags & RADEON_UPLOAD_LINE)        ? "line, " : "",
+		   (flags & RADEON_UPLOAD_BUMPMAP)     ? "bumpmap, " : "",
+		   (flags & RADEON_UPLOAD_MASKS)       ? "masks, " : "",
+		   (flags & RADEON_UPLOAD_VIEWPORT)    ? "viewport, " : "",
+		   (flags & RADEON_UPLOAD_SETUP)       ? "setup, " : "",
+		   (flags & RADEON_UPLOAD_TCL)         ? "tcl, " : "",
+		   (flags & RADEON_UPLOAD_MISC)        ? "misc, " : "",
+		   (flags & RADEON_UPLOAD_TEX0)        ? "tex0, " : "",
+		   (flags & RADEON_UPLOAD_TEX1)        ? "tex1, " : "",
+		   (flags & RADEON_UPLOAD_TEX2)        ? "tex2, " : "",
+		   (flags & RADEON_UPLOAD_CLIPRECTS)   ? "cliprects, " : "",
+		   (flags & RADEON_REQUIRE_QUIESCENCE) ? "quiescence, " : "" );
+}
+
+static void radeon_cp_dispatch_clear( drm_device_t *dev,
+				      drm_radeon_clear_t *clear )
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	int nbox = sarea_priv->nbox;
+	drm_clip_rect_t *pbox = sarea_priv->boxes;
+	unsigned int flags = clear->flags;
+	int i;
+	RING_LOCALS;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	radeon_update_ring_snapshot( dev_priv );
+
+	if ( dev_priv->page_flipping && dev_priv->current_page == 1 ) {
+		unsigned int tmp = flags;
+
+		flags &= ~(RADEON_FRONT | RADEON_BACK);
+		if ( tmp & RADEON_FRONT ) flags |= RADEON_BACK;
+		if ( tmp & RADEON_BACK )  flags |= RADEON_FRONT;
+	}
+
+	for ( i = 0 ; i < nbox ; i++ ) {
+		int x = pbox[i].x1;
+		int y = pbox[i].y1;
+		int w = pbox[i].x2 - x;
+		int h = pbox[i].y2 - y;
+
+		DRM_DEBUG( "dispatch clear %d,%d-%d,%d flags 0x%x\n",
+			   x, y, w, h, flags );
+
+		if ( flags & (RADEON_FRONT | RADEON_BACK) ) {
+			BEGIN_RING( 4 );
+
+			/* Ensure the 3D stream is idle before doing a
+			 * 2D fill to clear the front or back buffer.
+			 */
+			RADEON_WAIT_UNTIL_3D_IDLE();
+
+			OUT_RING( CP_PACKET0( RADEON_DP_WRITE_MASK, 0 ) );
+			OUT_RING( sarea_priv->context_state.rb3d_planemask );
+
+			ADVANCE_RING();
+
+			/* Make sure we restore the 3D state next time.
+			 */
+			dev_priv->sarea_priv->dirty |= (RADEON_UPLOAD_CONTEXT |
+							RADEON_UPLOAD_MASKS);
+		}
+
+		if ( flags & RADEON_FRONT ) {
+			BEGIN_RING( 6 );
+
+			OUT_RING( CP_PACKET3( RADEON_CNTL_PAINT_MULTI, 4 ) );
+			OUT_RING( RADEON_GMC_DST_PITCH_OFFSET_CNTL |
+				  RADEON_GMC_BRUSH_SOLID_COLOR |
+				  (dev_priv->color_fmt << 8) |
+				  RADEON_GMC_SRC_DATATYPE_COLOR |
+				  RADEON_ROP3_P |
+				  RADEON_GMC_CLR_CMP_CNTL_DIS );
+
+			OUT_RING( dev_priv->front_pitch_offset );
+			OUT_RING( clear->clear_color );
+
+			OUT_RING( (x << 16) | y );
+			OUT_RING( (w << 16) | h );
+
+			ADVANCE_RING();
+		}
+
+		if ( flags & RADEON_BACK ) {
+			BEGIN_RING( 6 );
+
+			OUT_RING( CP_PACKET3( RADEON_CNTL_PAINT_MULTI, 4 ) );
+			OUT_RING( RADEON_GMC_DST_PITCH_OFFSET_CNTL |
+				  RADEON_GMC_BRUSH_SOLID_COLOR |
+				  (dev_priv->color_fmt << 8) |
+				  RADEON_GMC_SRC_DATATYPE_COLOR |
+				  RADEON_ROP3_P |
+				  RADEON_GMC_CLR_CMP_CNTL_DIS );
+
+			OUT_RING( dev_priv->back_pitch_offset );
+			OUT_RING( clear->clear_color );
+
+			OUT_RING( (x << 16) | y );
+			OUT_RING( (w << 16) | h );
+
+			ADVANCE_RING();
+
+		}
+
+		if ( flags & RADEON_DEPTH ) {
+			drm_radeon_depth_clear_t *depth_clear =
+			   &dev_priv->depth_clear;
+
+			if ( sarea_priv->dirty & ~RADEON_UPLOAD_CLIPRECTS ) {
+				radeon_emit_state( dev_priv );
+			}
+
+			/* FIXME: Render a rectangle to clear the depth
+			 * buffer.  So much for those "fast Z clears"...
+			 */
+			BEGIN_RING( 23 );
+
+			RADEON_WAIT_UNTIL_2D_IDLE();
+
+			OUT_RING( CP_PACKET0( RADEON_PP_CNTL, 1 ) );
+			OUT_RING( 0x00000000 );
+			OUT_RING( depth_clear->rb3d_cntl );
+			OUT_RING( CP_PACKET0( RADEON_RB3D_ZSTENCILCNTL, 0 ) );
+			OUT_RING( depth_clear->rb3d_zstencilcntl );
+			OUT_RING( CP_PACKET0( RADEON_RB3D_PLANEMASK, 0 ) );
+			OUT_RING( 0x00000000 );
+			OUT_RING( CP_PACKET0( RADEON_SE_CNTL, 0 ) );
+			OUT_RING( depth_clear->se_cntl );
+
+			OUT_RING( CP_PACKET3( RADEON_3D_DRAW_IMMD, 10 ) );
+			OUT_RING( RADEON_VTX_Z_PRESENT );
+			OUT_RING( (RADEON_PRIM_TYPE_RECT_LIST |
+				   RADEON_PRIM_WALK_RING |
+				   RADEON_MAOS_ENABLE |
+				   RADEON_VTX_FMT_RADEON_MODE |
+				   (3 << RADEON_NUM_VERTICES_SHIFT)) );
+
+			OUT_RING( clear->rect.ui[CLEAR_X1] );
+			OUT_RING( clear->rect.ui[CLEAR_Y1] );
+			OUT_RING( clear->rect.ui[CLEAR_DEPTH] );
+
+			OUT_RING( clear->rect.ui[CLEAR_X1] );
+			OUT_RING( clear->rect.ui[CLEAR_Y2] );
+			OUT_RING( clear->rect.ui[CLEAR_DEPTH] );
+
+			OUT_RING( clear->rect.ui[CLEAR_X2] );
+			OUT_RING( clear->rect.ui[CLEAR_Y2] );
+			OUT_RING( clear->rect.ui[CLEAR_DEPTH] );
+
+			ADVANCE_RING();
+
+			/* Make sure we restore the 3D state next time.
+			 */
+			dev_priv->sarea_priv->dirty |= (RADEON_UPLOAD_CONTEXT |
+							RADEON_UPLOAD_SETUP |
+							RADEON_UPLOAD_MASKS);
+		}
+	}
+
+	/* Increment the clear counter.  The client-side 3D driver must
+	 * wait on this value before performing the clear ioctl.  We
+	 * need this because the card's so damned fast...
+	 */
+	dev_priv->sarea_priv->last_clear++;
+
+	BEGIN_RING( 4 );
+
+	RADEON_CLEAR_AGE( dev_priv->sarea_priv->last_clear );
+	RADEON_WAIT_UNTIL_IDLE();
+
+	ADVANCE_RING();
+}
+
+static void radeon_cp_dispatch_swap( drm_device_t *dev )
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	int nbox = sarea_priv->nbox;
+	drm_clip_rect_t *pbox = sarea_priv->boxes;
+	int i;
+	RING_LOCALS;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	radeon_update_ring_snapshot( dev_priv );
+
+#if RADEON_PERFORMANCE_BOXES
+	/* Do some trivial performance monitoring...
+	 */
+	radeon_cp_performance_boxes( dev_priv );
+#endif
+
+	/* Wait for the 3D stream to idle before dispatching the bitblt.
+	 * This will prevent data corruption between the two streams.
+	 */
+	BEGIN_RING( 2 );
+
+	RADEON_WAIT_UNTIL_3D_IDLE();
+
+	ADVANCE_RING();
+
+	for ( i = 0 ; i < nbox ; i++ ) {
+		int x = pbox[i].x1;
+		int y = pbox[i].y1;
+		int w = pbox[i].x2 - x;
+		int h = pbox[i].y2 - y;
+
+		DRM_DEBUG( "dispatch swap %d,%d-%d,%d\n",
+			   x, y, w, h );
+
+		BEGIN_RING( 7 );
+
+		OUT_RING( CP_PACKET3( RADEON_CNTL_BITBLT_MULTI, 5 ) );
+		OUT_RING( RADEON_GMC_SRC_PITCH_OFFSET_CNTL |
+			  RADEON_GMC_DST_PITCH_OFFSET_CNTL |
+			  RADEON_GMC_BRUSH_NONE |
+			  (dev_priv->color_fmt << 8) |
+			  RADEON_GMC_SRC_DATATYPE_COLOR |
+			  RADEON_ROP3_S |
+			  RADEON_DP_SRC_SOURCE_MEMORY |
+			  RADEON_GMC_CLR_CMP_CNTL_DIS |
+			  RADEON_GMC_WR_MSK_DIS );
+
+		OUT_RING( dev_priv->back_pitch_offset );
+		OUT_RING( dev_priv->front_pitch_offset );
+
+		OUT_RING( (x << 16) | y );
+		OUT_RING( (x << 16) | y );
+		OUT_RING( (w << 16) | h );
+
+		ADVANCE_RING();
+	}
+
+	/* Increment the frame counter.  The client-side 3D driver must
+	 * throttle the framerate by waiting for this value before
+	 * performing the swapbuffer ioctl.
+	 */
+	dev_priv->sarea_priv->last_frame++;
+
+	BEGIN_RING( 4 );
+
+	RADEON_FRAME_AGE( dev_priv->sarea_priv->last_frame );
+	RADEON_WAIT_UNTIL_2D_IDLE();
+
+	ADVANCE_RING();
+}
+
+static void radeon_cp_dispatch_flip( drm_device_t *dev )
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	RING_LOCALS;
+	DRM_DEBUG( "%s: page=%d\n", __FUNCTION__, dev_priv->current_page );
+
+	radeon_update_ring_snapshot( dev_priv );
+
+#if RADEON_PERFORMANCE_BOXES
+	/* Do some trivial performance monitoring...
+	 */
+	radeon_cp_performance_boxes( dev_priv );
+#endif
+
+	BEGIN_RING( 6 );
+
+	RADEON_WAIT_UNTIL_3D_IDLE();
+	RADEON_WAIT_UNTIL_PAGE_FLIPPED();
+
+	OUT_RING( CP_PACKET0( RADEON_CRTC_OFFSET, 0 ) );
+
+	if ( dev_priv->current_page == 0 ) {
+		OUT_RING( dev_priv->back_offset );
+		dev_priv->current_page = 1;
+	} else {
+		OUT_RING( dev_priv->front_offset );
+		dev_priv->current_page = 0;
+	}
+
+	ADVANCE_RING();
+
+	/* Increment the frame counter.  The client-side 3D driver must
+	 * throttle the framerate by waiting for this value before
+	 * performing the swapbuffer ioctl.
+	 */
+	dev_priv->sarea_priv->last_frame++;
+
+	BEGIN_RING( 2 );
+
+	RADEON_FRAME_AGE( dev_priv->sarea_priv->last_frame );
+
+	ADVANCE_RING();
+}
+
+static void radeon_cp_dispatch_vertex( drm_device_t *dev,
+				       drm_buf_t *buf )
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	drm_radeon_buf_priv_t *buf_priv = buf->dev_private;
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	int format = sarea_priv->vc_format;
+	int offset = dev_priv->agp_buffers_offset + buf->offset;
+	int size = buf->used;
+	int prim = buf_priv->prim;
+	int i = 0;
+	RING_LOCALS;
+	DRM_DEBUG( "%s: nbox=%d\n", __FUNCTION__, sarea_priv->nbox );
+
+	radeon_update_ring_snapshot( dev_priv );
+
+	if ( 0 )
+		radeon_print_dirty( "dispatch_vertex", sarea_priv->dirty );
+
+	if ( buf->used ) {
+		buf_priv->dispatched = 1;
+
+		if ( sarea_priv->dirty & ~RADEON_UPLOAD_CLIPRECTS ) {
+			radeon_emit_state( dev_priv );
+		}
+
+		do {
+			/* Emit the next set of up to three cliprects */
+			if ( i < sarea_priv->nbox ) {
+				radeon_emit_clip_rect( dev_priv,
+						       &sarea_priv->boxes[i] );
+			}
+
+			/* Emit the vertex buffer rendering commands */
+			BEGIN_RING( 5 );
+
+			OUT_RING( CP_PACKET3( RADEON_3D_RNDR_GEN_INDX_PRIM, 3 ) );
+			OUT_RING( offset );
+			OUT_RING( size );
+			OUT_RING( format );
+			OUT_RING( prim | RADEON_PRIM_WALK_LIST |
+				  RADEON_COLOR_ORDER_RGBA |
+				  RADEON_VTX_FMT_RADEON_MODE |
+				  (size << RADEON_NUM_VERTICES_SHIFT) );
+
+			ADVANCE_RING();
+
+			i++;
+		} while ( i < sarea_priv->nbox );
+	}
+
+	if ( buf_priv->discard ) {
+		buf_priv->age = dev_priv->sarea_priv->last_dispatch;
+
+		/* Emit the vertex buffer age */
+		BEGIN_RING( 2 );
+		RADEON_DISPATCH_AGE( buf_priv->age );
+		ADVANCE_RING();
+
+		buf->pending = 1;
+		buf->used = 0;
+		/* FIXME: Check dispatched field */
+		buf_priv->dispatched = 0;
+	}
+
+	dev_priv->sarea_priv->last_dispatch++;
+
+	sarea_priv->dirty &= ~RADEON_UPLOAD_CLIPRECTS;
+	sarea_priv->nbox = 0;
+}
+
+
+static void radeon_cp_dispatch_indirect( drm_device_t *dev,
+					 drm_buf_t *buf,
+					 int start, int end )
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	drm_radeon_buf_priv_t *buf_priv = buf->dev_private;
+	RING_LOCALS;
+	DRM_DEBUG( "indirect: buf=%d s=0x%x e=0x%x\n",
+		   buf->idx, start, end );
+
+	radeon_update_ring_snapshot( dev_priv );
+
+	if ( start != end ) {
+		int offset = (dev_priv->agp_buffers_offset
+			      + buf->offset + start);
+		int dwords = (end - start + 3) / sizeof(u32);
+
+		/* Indirect buffer data must be an even number of
+		 * dwords, so if we've been given an odd number we must
+		 * pad the data with a Type-2 CP packet.
+		 */
+		if ( dwords & 1 ) {
+			u32 *data = (u32 *)
+				((char *)dev_priv->buffers->handle
+				 + buf->offset + start);
+			data[dwords++] = RADEON_CP_PACKET2;
+		}
+
+		buf_priv->dispatched = 1;
+
+		/* Fire off the indirect buffer */
+		BEGIN_RING( 3 );
+
+		OUT_RING( CP_PACKET0( RADEON_CP_IB_BASE, 1 ) );
+		OUT_RING( offset );
+		OUT_RING( dwords );
+
+		ADVANCE_RING();
+	}
+
+	if ( buf_priv->discard ) {
+		buf_priv->age = dev_priv->sarea_priv->last_dispatch;
+
+		/* Emit the indirect buffer age */
+		BEGIN_RING( 2 );
+		RADEON_DISPATCH_AGE( buf_priv->age );
+		ADVANCE_RING();
+
+		buf->pending = 1;
+		buf->used = 0;
+		/* FIXME: Check dispatched field */
+		buf_priv->dispatched = 0;
+	}
+
+	dev_priv->sarea_priv->last_dispatch++;
+}
+
+static void radeon_cp_dispatch_indices( drm_device_t *dev,
+					drm_buf_t *buf,
+					int start, int end,
+					int count )
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	drm_radeon_buf_priv_t *buf_priv = buf->dev_private;
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	int format = sarea_priv->vc_format;
+	int offset = dev_priv->agp_buffers_offset;
+	int prim = buf_priv->prim;
+	u32 *data;
+	int dwords;
+	int i = 0;
+	RING_LOCALS;
+	DRM_DEBUG( "indices: s=%d e=%d c=%d\n", start, end, count );
+
+	radeon_update_ring_snapshot( dev_priv );
+
+	if ( 0 )
+		radeon_print_dirty( "dispatch_indices", sarea_priv->dirty );
+
+	if ( start != end ) {
+		buf_priv->dispatched = 1;
+
+		if ( sarea_priv->dirty & ~RADEON_UPLOAD_CLIPRECTS ) {
+			radeon_emit_state( dev_priv );
+		}
+
+		dwords = (end - start + 3) / sizeof(u32);
+
+		data = (u32 *)((char *)dev_priv->buffers->handle
+			       + buf->offset + start);
+
+		data[0] = CP_PACKET3( RADEON_3D_RNDR_GEN_INDX_PRIM, dwords-2 );
+
+		data[1] = offset;
+		data[2] = RADEON_MAX_VB_VERTS;
+		data[3] = format;
+		data[4] = (prim | RADEON_PRIM_WALK_IND |
+			   RADEON_COLOR_ORDER_RGBA |
+			   RADEON_VTX_FMT_RADEON_MODE |
+			   (count << RADEON_NUM_VERTICES_SHIFT) );
+
+		if ( count & 0x1 ) {
+			data[dwords-1] &= 0x0000ffff;
+		}
+
+		do {
+			/* Emit the next set of up to three cliprects */
+			if ( i < sarea_priv->nbox ) {
+				radeon_emit_clip_rect( dev_priv,
+						       &sarea_priv->boxes[i] );
+			}
+
+			radeon_cp_dispatch_indirect( dev, buf, start, end );
+
+			i++;
+		} while ( i < sarea_priv->nbox );
+	}
+
+	if ( buf_priv->discard ) {
+		buf_priv->age = dev_priv->sarea_priv->last_dispatch;
+
+		/* Emit the vertex buffer age */
+		BEGIN_RING( 2 );
+		RADEON_DISPATCH_AGE( buf_priv->age );
+		ADVANCE_RING();
+
+		buf->pending = 1;
+		/* FIXME: Check dispatched field */
+		buf_priv->dispatched = 0;
+	}
+
+	dev_priv->sarea_priv->last_dispatch++;
+
+	sarea_priv->dirty &= ~RADEON_UPLOAD_CLIPRECTS;
+	sarea_priv->nbox = 0;
+}
+
+static int radeon_cp_dispatch_blit( drm_device_t *dev,
+				    drm_radeon_blit_t *blit )
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	drm_device_dma_t *dma = dev->dma;
+	drm_buf_t *buf;
+	drm_radeon_buf_priv_t *buf_priv;
+	u32 format;
+	u32 *data;
+	int dword_shift, dwords;
+	RING_LOCALS;
+	DRM_DEBUG( "blit: ofs=0x%x p=%d f=%d x=%hd y=%hd w=%hd h=%hd\n",
+		   blit->offset >> 10, blit->pitch, blit->format,
+		   blit->x, blit->y, blit->width, blit->height );
+
+	radeon_update_ring_snapshot( dev_priv );
+
+	/* The compiler won't optimize away a division by a variable,
+	 * even if the only legal values are powers of two.  Thus, we'll
+	 * use a shift instead.
+	 */
+	switch ( blit->format ) {
+	case RADEON_TXF_32BPP_ARGB8888:
+	case RADEON_TXF_32BPP_RGBA8888:
+		format = RADEON_COLOR_FORMAT_ARGB8888;
+		dword_shift = 0;
+		break;
+	case RADEON_TXF_16BPP_AI88:
+	case RADEON_TXF_16BPP_ARGB1555:
+	case RADEON_TXF_16BPP_RGB565:
+	case RADEON_TXF_16BPP_ARGB4444:
+		format = RADEON_COLOR_FORMAT_RGB565;
+		dword_shift = 1;
+		break;
+	case RADEON_TXF_8BPP_I:
+	case RADEON_TXF_8BPP_RGB332:
+		format = RADEON_COLOR_FORMAT_CI8;
+		dword_shift = 2;
+		break;
+	default:
+		DRM_ERROR( "invalid blit format %d\n", blit->format );
+		return -EINVAL;
+	}
+
+	/* Flush the pixel cache.  This ensures no pixel data gets mixed
+	 * up with the texture data from the host data blit, otherwise
+	 * part of the texture image may be corrupted.
+	 */
+	BEGIN_RING( 4 );
+
+	RADEON_FLUSH_CACHE();
+	RADEON_WAIT_UNTIL_IDLE();
+
+	ADVANCE_RING();
+
+	/* Dispatch the indirect buffer.
+	 */
+	buf = dma->buflist[blit->idx];
+	buf_priv = buf->dev_private;
+
+	if ( buf->pid != current->pid ) {
+		DRM_ERROR( "process %d using buffer owned by %d\n",
+			   current->pid, buf->pid );
+		return -EINVAL;
+	}
+	if ( buf->pending ) {
+		DRM_ERROR( "sending pending buffer %d\n", blit->idx );
+		return -EINVAL;
+	}
+
+	buf_priv->discard = 1;
+
+	dwords = (blit->width * blit->height) >> dword_shift;
+	if ( !dwords ) dwords = 1;
+
+	data = (u32 *)((char *)dev_priv->buffers->handle + buf->offset);
+
+	data[0] = CP_PACKET3( RADEON_CNTL_HOSTDATA_BLT, dwords + 6 );
+	data[1] = (RADEON_GMC_DST_PITCH_OFFSET_CNTL |
+		   RADEON_GMC_BRUSH_NONE |
+		   (format << 8) |
+		   RADEON_GMC_SRC_DATATYPE_COLOR |
+		   RADEON_ROP3_S |
+		   RADEON_DP_SRC_SOURCE_HOST_DATA |
+		   RADEON_GMC_CLR_CMP_CNTL_DIS |
+		   RADEON_GMC_WR_MSK_DIS);
+
+	data[2] = (blit->pitch << 22) | (blit->offset >> 10);
+	data[3] = 0xffffffff;
+	data[4] = 0xffffffff;
+	data[5] = (blit->y << 16) | blit->x;
+	data[6] = (blit->height << 16) | blit->width;
+	data[7] = dwords;
+
+	buf->used = (dwords + 8) * sizeof(u32);
+
+	radeon_cp_dispatch_indirect( dev, buf, 0, buf->used );
+
+	/* Flush the pixel cache after the blit completes.  This ensures
+	 * the texture data is written out to memory before rendering
+	 * continues.
+	 */
+	BEGIN_RING( 4 );
+
+	RADEON_FLUSH_CACHE();
+	RADEON_WAIT_UNTIL_2D_IDLE();
+
+	ADVANCE_RING();
+
+	return 0;
+}
+
+static void radeon_cp_dispatch_stipple( drm_device_t *dev, u32 *stipple )
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	int i;
+	RING_LOCALS;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	radeon_update_ring_snapshot( dev_priv );
+
+	BEGIN_RING( 35 );
+
+	OUT_RING( CP_PACKET0( RADEON_RE_STIPPLE_ADDR, 0 ) );
+	OUT_RING( 0x00000000 );
+
+	OUT_RING( CP_PACKET0_TABLE( RADEON_RE_STIPPLE_DATA, 31 ) );
+	for ( i = 0 ; i < 32 ; i++ ) {
+		OUT_RING( stipple[i] );
+	}
+
+	ADVANCE_RING();
+}
+
+
+/* ================================================================
+ * IOCTL functions
+ */
+
+int radeon_cp_clear( struct inode *inode, struct file *filp,
+		     unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_radeon_clear_t clear;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &clear, (drm_radeon_clear_t *) arg,
+			     sizeof(clear) ) )
+		return -EFAULT;
+
+	if ( sarea_priv->nbox > RADEON_NR_SAREA_CLIPRECTS )
+		sarea_priv->nbox = RADEON_NR_SAREA_CLIPRECTS;
+
+	radeon_cp_dispatch_clear( dev, &clear );
+
+	return 0;
+}
+
+int radeon_cp_swap( struct inode *inode, struct file *filp,
+		    unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( sarea_priv->nbox > RADEON_NR_SAREA_CLIPRECTS )
+		sarea_priv->nbox = RADEON_NR_SAREA_CLIPRECTS;
+
+	if ( !dev_priv->page_flipping ) {
+		radeon_cp_dispatch_swap( dev );
+		dev_priv->sarea_priv->dirty |= (RADEON_UPLOAD_CONTEXT |
+						RADEON_UPLOAD_MASKS);
+	} else {
+		radeon_cp_dispatch_flip( dev );
+	}
+
+	return 0;
+}
+
+int radeon_cp_vertex( struct inode *inode, struct file *filp,
+		      unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	drm_device_dma_t *dma = dev->dma;
+	drm_buf_t *buf;
+	drm_radeon_buf_priv_t *buf_priv;
+	drm_radeon_vertex_t vertex;
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+	if ( !dev_priv || dev_priv->is_pci ) {
+		DRM_ERROR( "%s called with a PCI card\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &vertex, (drm_radeon_vertex_t *)arg,
+			     sizeof(vertex) ) )
+		return -EFAULT;
+
+	DRM_DEBUG( "%s: pid=%d index=%d count=%d discard=%d\n",
+		   __FUNCTION__, current->pid,
+		   vertex.idx, vertex.count, vertex.discard );
+
+	if ( vertex.idx < 0 || vertex.idx >= dma->buf_count ) {
+		DRM_ERROR( "buffer index %d (of %d max)\n",
+			   vertex.idx, dma->buf_count - 1 );
+		return -EINVAL;
+	}
+	if ( vertex.prim < 0 ||
+	     vertex.prim > RADEON_PRIM_TYPE_3VRT_LINE_LIST ) {
+		DRM_ERROR( "buffer prim %d\n", vertex.prim );
+		return -EINVAL;
+	}
+
+	VB_AGE_CHECK_WITH_RET( dev_priv );
+
+	buf = dma->buflist[vertex.idx];
+	buf_priv = buf->dev_private;
+
+	if ( buf->pid != current->pid ) {
+		DRM_ERROR( "process %d using buffer owned by %d\n",
+			   current->pid, buf->pid );
+		return -EINVAL;
+	}
+	if ( buf->pending ) {
+		DRM_ERROR( "sending pending buffer %d\n", vertex.idx );
+		return -EINVAL;
+	}
+
+	buf->used = vertex.count;
+	buf_priv->prim = vertex.prim;
+	buf_priv->discard = vertex.discard;
+
+	radeon_cp_dispatch_vertex( dev, buf );
+
+	return 0;
+}
+
+int radeon_cp_indices( struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	drm_device_dma_t *dma = dev->dma;
+	drm_buf_t *buf;
+	drm_radeon_buf_priv_t *buf_priv;
+	drm_radeon_indices_t elts;
+	int count;
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+	if ( !dev_priv || dev_priv->is_pci ) {
+		DRM_ERROR( "%s called with a PCI card\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &elts, (drm_radeon_indices_t *)arg,
+			     sizeof(elts) ) )
+		return -EFAULT;
+
+	DRM_DEBUG( "%s: pid=%d index=%d start=%d end=%d discard=%d\n",
+		   __FUNCTION__, current->pid,
+		   elts.idx, elts.start, elts.end, elts.discard );
+
+	if ( elts.idx < 0 || elts.idx >= dma->buf_count ) {
+		DRM_ERROR( "buffer index %d (of %d max)\n",
+			   elts.idx, dma->buf_count - 1 );
+		return -EINVAL;
+	}
+	if ( elts.prim < 0 ||
+	     elts.prim > RADEON_PRIM_TYPE_3VRT_LINE_LIST ) {
+		DRM_ERROR( "buffer prim %d\n", elts.prim );
+		return -EINVAL;
+	}
+
+	VB_AGE_CHECK_WITH_RET( dev_priv );
+
+	buf = dma->buflist[elts.idx];
+	buf_priv = buf->dev_private;
+
+	if ( buf->pid != current->pid ) {
+		DRM_ERROR( "process %d using buffer owned by %d\n",
+			   current->pid, buf->pid );
+		return -EINVAL;
+	}
+	if ( buf->pending ) {
+		DRM_ERROR( "sending pending buffer %d\n", elts.idx );
+		return -EINVAL;
+	}
+
+	count = (elts.end - elts.start) / sizeof(u16);
+	elts.start -= RADEON_INDEX_PRIM_OFFSET;
+
+	if ( elts.start & 0x7 ) {
+		DRM_ERROR( "misaligned buffer 0x%x\n", elts.start );
+		return -EINVAL;
+	}
+	if ( elts.start < buf->used ) {
+		DRM_ERROR( "no header 0x%x - 0x%x\n", elts.start, buf->used );
+		return -EINVAL;
+	}
+
+	buf->used = elts.end;
+	buf_priv->prim = elts.prim;
+	buf_priv->discard = elts.discard;
+
+	radeon_cp_dispatch_indices( dev, buf, elts.start, elts.end, count );
+
+	return 0;
+}
+
+int radeon_cp_blit( struct inode *inode, struct file *filp,
+		    unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	drm_device_dma_t *dma = dev->dma;
+	drm_radeon_blit_t blit;
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &blit, (drm_radeon_blit_t *)arg,
+			     sizeof(blit) ) )
+		return -EFAULT;
+
+	DRM_DEBUG( "%s: pid=%d index=%d\n",
+		   __FUNCTION__, current->pid, blit.idx );
+
+	if ( blit.idx < 0 || blit.idx > dma->buf_count ) {
+		DRM_ERROR( "sending %d buffers (of %d max)\n",
+			   blit.idx, dma->buf_count );
+		return -EINVAL;
+	}
+
+	VB_AGE_CHECK_WITH_RET( dev_priv );
+
+	return radeon_cp_dispatch_blit( dev, &blit );
+}
+
+int radeon_cp_stipple( struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_radeon_stipple_t stipple;
+	u32 mask[32];
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &stipple, (drm_radeon_stipple_t *)arg,
+			     sizeof(stipple) ) )
+		return -EFAULT;
+
+	if ( copy_from_user( &mask, stipple.mask,
+			     32 * sizeof(u32) ) )
+		return -EFAULT;
+
+	radeon_cp_dispatch_stipple( dev, mask );
+
+	return 0;
+}
+
+int radeon_cp_indirect( struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	drm_device_dma_t *dma = dev->dma;
+	drm_buf_t *buf;
+	drm_radeon_buf_priv_t *buf_priv;
+	drm_radeon_indirect_t indirect;
+	RING_LOCALS;
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+	if ( !dev_priv || dev_priv->is_pci ) {
+		DRM_ERROR( "%s called with a PCI card\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &indirect, (drm_radeon_indirect_t *)arg,
+			     sizeof(indirect) ) )
+		return -EFAULT;
+
+	DRM_DEBUG( "indirect: idx=%d s=%d e=%d d=%d\n",
+		   indirect.idx, indirect.start,
+		   indirect.end, indirect.discard );
+
+	if ( indirect.idx < 0 || indirect.idx >= dma->buf_count ) {
+		DRM_ERROR( "buffer index %d (of %d max)\n",
+			   indirect.idx, dma->buf_count - 1 );
+		return -EINVAL;
+	}
+
+	buf = dma->buflist[indirect.idx];
+	buf_priv = buf->dev_private;
+
+	if ( buf->pid != current->pid ) {
+		DRM_ERROR( "process %d using buffer owned by %d\n",
+			   current->pid, buf->pid );
+		return -EINVAL;
+	}
+	if ( buf->pending ) {
+		DRM_ERROR( "sending pending buffer %d\n", indirect.idx );
+		return -EINVAL;
+	}
+
+	if ( indirect.start < buf->used ) {
+		DRM_ERROR( "reusing indirect: start=0x%x actual=0x%x\n",
+			   indirect.start, buf->used );
+		return -EINVAL;
+	}
+
+	VB_AGE_CHECK_WITH_RET( dev_priv );
+
+	buf->used = indirect.end;
+	buf_priv->discard = indirect.discard;
+
+	/* Wait for the 3D stream to idle before the indirect buffer
+	 * containing 2D acceleration commands is processed.
+	 */
+	BEGIN_RING( 2 );
+
+	RADEON_WAIT_UNTIL_3D_IDLE();
+
+	ADVANCE_RING();
+
+	/* Dispatch the indirect buffer full of commands from the
+	 * X server.  This is insecure and is thus only available to
+	 * privileged clients.
+	 */
+	radeon_cp_dispatch_indirect( dev, buf, indirect.start, indirect.end );
+
+	return 0;
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/tdfx_context.c linux/drivers/char/drm-4.0/tdfx_context.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/tdfx_context.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/tdfx_context.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,219 @@
+/* tdfx_context.c -- IOCTLs for tdfx contexts -*- linux-c -*-
+ * Created: Thu Oct  7 10:50:22 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *    Daryll Strauss <daryll@valinux.com>
+ * 
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "tdfx_drv.h"
+
+extern drm_ctx_t tdfx_res_ctx;
+
+static int tdfx_alloc_queue(drm_device_t *dev)
+{
+	return drm_ctxbitmap_next(dev);
+}
+
+int tdfx_context_switch(drm_device_t *dev, int old, int new)
+{
+        char        buf[64];
+
+        atomic_inc(&dev->total_ctx);
+
+        if (test_and_set_bit(0, &dev->context_flag)) {
+                DRM_ERROR("Reentering -- FIXME\n");
+                return -EBUSY;
+        }
+
+#if DRM_DMA_HISTOGRAM
+        dev->ctx_start = get_cycles();
+#endif
+        
+        DRM_DEBUG("Context switch from %d to %d\n", old, new);
+
+        if (new == dev->last_context) {
+                clear_bit(0, &dev->context_flag);
+                return 0;
+        }
+        
+        if (drm_flags & DRM_FLAG_NOCTX) {
+                tdfx_context_switch_complete(dev, new);
+        } else {
+                sprintf(buf, "C %d %d\n", old, new);
+                drm_write_string(dev, buf);
+        }
+        
+        return 0;
+}
+
+int tdfx_context_switch_complete(drm_device_t *dev, int new)
+{
+        dev->last_context = new;  /* PRE/POST: This is the _only_ writer. */
+        dev->last_switch  = jiffies;
+        
+        if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+                DRM_ERROR("Lock isn't held after context switch\n");
+        }
+
+				/* If a context switch is ever initiated
+                                   when the kernel holds the lock, release
+                                   that lock here. */
+#if DRM_DMA_HISTOGRAM
+        atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles()
+                                                      - dev->ctx_start)]);
+                   
+#endif
+        clear_bit(0, &dev->context_flag);
+        wake_up(&dev->context_wait);
+        
+        return 0;
+}
+
+
+int tdfx_resctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_ctx_res_t	res;
+	drm_ctx_t	ctx;
+	int		i;
+
+	DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS);
+	if (copy_from_user(&res, (drm_ctx_res_t *)arg, sizeof(res)))
+		return -EFAULT;
+	if (res.count >= DRM_RESERVED_CONTEXTS) {
+		memset(&ctx, 0, sizeof(ctx));
+		for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
+			ctx.handle = i;
+			if (copy_to_user(&res.contexts[i],
+					 &i,
+					 sizeof(i)))
+				return -EFAULT;
+		}
+	}
+	res.count = DRM_RESERVED_CONTEXTS;
+	if (copy_to_user((drm_ctx_res_t *)arg, &res, sizeof(res)))
+		return -EFAULT;
+	return 0;
+}
+
+
+int tdfx_addctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	if ((ctx.handle = tdfx_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) {
+				/* Skip kernel's context and get a new one. */
+		ctx.handle = tdfx_alloc_queue(dev);
+	}
+	DRM_DEBUG("%d\n", ctx.handle);
+	if (ctx.handle == -1) {
+		DRM_DEBUG("Not enough free contexts.\n");
+				/* Should this return -EBUSY instead? */
+		return -ENOMEM;
+	}
+   
+	if (copy_to_user((drm_ctx_t *)arg, &ctx, sizeof(ctx)))
+		return -EFAULT;
+	return 0;
+}
+
+int tdfx_modctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	unsigned long arg)
+{
+	drm_ctx_t ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx)))
+		return -EFAULT;
+	if (ctx.flags==_DRM_CONTEXT_PRESERVED)
+		tdfx_res_ctx.handle=ctx.handle;
+	return 0;
+}
+
+int tdfx_getctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	unsigned long arg)
+{
+	drm_ctx_t ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx)))
+		return -EFAULT;
+	/* This is 0, because we don't handle any context flags */
+	ctx.flags = 0;
+	if (copy_to_user((drm_ctx_t*)arg, &ctx, sizeof(ctx)))
+		return -EFAULT;
+	return 0;
+}
+
+int tdfx_switchctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		   unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	return tdfx_context_switch(dev, dev->last_context, ctx.handle);
+}
+
+int tdfx_newctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	tdfx_context_switch_complete(dev, ctx.handle);
+
+	return 0;
+}
+
+int tdfx_rmctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t      *priv   = filp->private_data;
+	drm_device_t    *dev    = priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	drm_ctxbitmap_free(dev, ctx.handle);
+
+	return 0;
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/tdfx_drv.c linux/drivers/char/drm-4.0/tdfx_drv.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/tdfx_drv.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/tdfx_drv.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,697 @@
+/* tdfx_drv.c -- tdfx driver -*- linux-c -*-
+ * Created: Thu Oct  7 10:38:32 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *    Daryll Strauss <daryll@valinux.com>
+ *
+ */
+
+#include <linux/config.h>
+#include "drmP.h"
+#include "tdfx_drv.h"
+
+#define TDFX_NAME	 "tdfx"
+#define TDFX_DESC	 "3dfx Banshee/Voodoo3+"
+#define TDFX_DATE	 "20000928"
+#define TDFX_MAJOR	 1
+#define TDFX_MINOR	 0
+#define TDFX_PATCHLEVEL  0
+
+static drm_device_t	      tdfx_device;
+drm_ctx_t	              tdfx_res_ctx;
+
+static struct file_operations tdfx_fops = {
+#if LINUX_VERSION_CODE >= 0x020400
+				/* This started being used during 2.4.0-test */
+	owner:   THIS_MODULE,
+#endif
+	open:	 tdfx_open,
+	flush:	 drm_flush,
+	release: tdfx_release,
+	ioctl:	 tdfx_ioctl,
+	mmap:	 drm_mmap,
+	read:	 drm_read,
+	fasync:	 drm_fasync,
+	poll:	 drm_poll,
+};
+
+static struct miscdevice      tdfx_misc = {
+	minor: MISC_DYNAMIC_MINOR,
+	name:  TDFX_NAME,
+	fops:  &tdfx_fops,
+};
+
+static drm_ioctl_desc_t	      tdfx_ioctls[] = {
+	[DRM_IOCTL_NR(DRM_IOCTL_VERSION)]    = { tdfx_version,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)] = { drm_getunique,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)]  = { drm_getmagic,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)]  = { drm_irq_busid,	  0, 1 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)] = { drm_setunique,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_BLOCK)]	     = { drm_block,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)]    = { drm_unblock,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)] = { drm_authmagic,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)]    = { drm_addmap,	  1, 1 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)]    = { tdfx_addctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)]     = { tdfx_rmctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)]    = { tdfx_modctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)]    = { tdfx_getctx,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { tdfx_switchctx,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)]    = { tdfx_newctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)]    = { tdfx_resctx,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)]   = { drm_adddraw,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)]    = { drm_rmdraw,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_LOCK)]	     = { tdfx_lock,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)]     = { tdfx_unlock,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FINISH)]     = { drm_finish,	  1, 0 },
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)]   = {drm_agp_acquire, 1, 1},
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)]   = {drm_agp_release, 1, 1},
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)]    = {drm_agp_enable,  1, 1},
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)]      = {drm_agp_info,    1, 1},
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)]     = {drm_agp_alloc,   1, 1},
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)]      = {drm_agp_free,    1, 1},
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)]      = {drm_agp_unbind,  1, 1},
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)]    = {drm_agp_bind,    1, 1},
+#endif
+};
+#define TDFX_IOCTL_COUNT DRM_ARRAY_SIZE(tdfx_ioctls)
+
+#ifdef MODULE
+static char		      *tdfx = NULL;
+#endif
+
+MODULE_AUTHOR("VA Linux Systems, Inc.");
+MODULE_LICENSE("GPL and additional rights");
+MODULE_DESCRIPTION("tdfx");
+MODULE_PARM(tdfx, "s");
+
+#ifndef MODULE
+/* tdfx_options is called by the kernel to parse command-line options
+ * passed via the boot-loader (e.g., LILO).  It calls the insmod option
+ * routine, drm_parse_drm.
+ */
+
+static int __init tdfx_options(char *str)
+{
+	drm_parse_options(str);
+	return 1;
+}
+
+__setup("tdfx=", tdfx_options);
+#endif
+
+static int tdfx_setup(drm_device_t *dev)
+{
+	int i;
+
+	atomic_set(&dev->ioctl_count, 0);
+	atomic_set(&dev->vma_count, 0);
+	dev->buf_use	  = 0;
+	atomic_set(&dev->buf_alloc, 0);
+
+	atomic_set(&dev->total_open, 0);
+	atomic_set(&dev->total_close, 0);
+	atomic_set(&dev->total_ioctl, 0);
+	atomic_set(&dev->total_irq, 0);
+	atomic_set(&dev->total_ctx, 0);
+	atomic_set(&dev->total_locks, 0);
+	atomic_set(&dev->total_unlocks, 0);
+	atomic_set(&dev->total_contends, 0);
+	atomic_set(&dev->total_sleeps, 0);
+
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		dev->magiclist[i].head = NULL;
+		dev->magiclist[i].tail = NULL;
+	}
+	dev->maplist	    = NULL;
+	dev->map_count	    = 0;
+	dev->vmalist	    = NULL;
+	dev->lock.hw_lock   = NULL;
+	init_waitqueue_head(&dev->lock.lock_queue);
+	dev->queue_count    = 0;
+	dev->queue_reserved = 0;
+	dev->queue_slots    = 0;
+	dev->queuelist	    = NULL;
+	dev->irq	    = 0;
+	dev->context_flag   = 0;
+	dev->interrupt_flag = 0;
+	dev->dma            = 0;
+	dev->dma_flag	    = 0;
+	dev->last_context   = 0;
+	dev->last_switch    = 0;
+	dev->last_checked   = 0;
+	init_timer(&dev->timer);
+	init_waitqueue_head(&dev->context_wait);
+
+	dev->ctx_start	    = 0;
+	dev->lck_start	    = 0;
+
+	dev->buf_rp	  = dev->buf;
+	dev->buf_wp	  = dev->buf;
+	dev->buf_end	  = dev->buf + DRM_BSZ;
+	dev->buf_async	  = NULL;
+	init_waitqueue_head(&dev->buf_readers);
+	init_waitqueue_head(&dev->buf_writers);
+
+	tdfx_res_ctx.handle=-1;
+
+	DRM_DEBUG("\n");
+
+	/* The kernel's context could be created here, but is now created
+	   in drm_dma_enqueue.	This is more resource-efficient for
+	   hardware that does not do DMA, but may mean that
+	   drm_select_queue fails between the time the interrupt is
+	   initialized and the time the queues are initialized. */
+
+	return 0;
+}
+
+
+static int tdfx_takedown(drm_device_t *dev)
+{
+	int		  i;
+	drm_magic_entry_t *pt, *next;
+	drm_map_t	  *map;
+	drm_vma_entry_t	  *vma, *vma_next;
+
+	DRM_DEBUG("\n");
+
+	down(&dev->struct_sem);
+	del_timer(&dev->timer);
+
+	if (dev->devname) {
+		drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER);
+		dev->devname = NULL;
+	}
+
+	if (dev->unique) {
+		drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER);
+		dev->unique = NULL;
+		dev->unique_len = 0;
+	}
+				/* Clear pid list */
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		for (pt = dev->magiclist[i].head; pt; pt = next) {
+			next = pt->next;
+			drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
+		}
+		dev->magiclist[i].head = dev->magiclist[i].tail = NULL;
+	}
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+				/* Clear AGP information */
+	if (dev->agp) {
+		drm_agp_mem_t *temp;
+		drm_agp_mem_t *temp_next;
+
+		temp = dev->agp->memory;
+		while(temp != NULL) {
+			temp_next = temp->next;
+			drm_free_agp(temp->memory, temp->pages);
+			drm_free(temp, sizeof(*temp), DRM_MEM_AGPLISTS);
+			temp = temp_next;
+		}
+		if (dev->agp->acquired) _drm_agp_release();
+	}
+#endif
+				/* Clear vma list (only built for debugging) */
+	if (dev->vmalist) {
+		for (vma = dev->vmalist; vma; vma = vma_next) {
+			vma_next = vma->next;
+			drm_free(vma, sizeof(*vma), DRM_MEM_VMAS);
+		}
+		dev->vmalist = NULL;
+	}
+
+				/* Clear map area and mtrr information */
+	if (dev->maplist) {
+		for (i = 0; i < dev->map_count; i++) {
+			map = dev->maplist[i];
+			switch (map->type) {
+			case _DRM_REGISTERS:
+			case _DRM_FRAME_BUFFER:
+#ifdef CONFIG_MTRR
+				if (map->mtrr >= 0) {
+					int retcode;
+					retcode = mtrr_del(map->mtrr,
+							   map->offset,
+							   map->size);
+					DRM_DEBUG("mtrr_del = %d\n", retcode);
+				}
+#endif
+				drm_ioremapfree(map->handle, map->size);
+				break;
+			case _DRM_SHM:
+				drm_free_pages((unsigned long)map->handle,
+					       drm_order(map->size)
+					       - PAGE_SHIFT,
+					       DRM_MEM_SAREA);
+				break;
+			case _DRM_AGP:
+				/* Do nothing here, because this is all
+                                   handled in the AGP/GART driver. */
+				break;
+			}
+			drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+		}
+		drm_free(dev->maplist,
+			 dev->map_count * sizeof(*dev->maplist),
+			 DRM_MEM_MAPS);
+		dev->maplist   = NULL;
+		dev->map_count = 0;
+	}
+
+	if (dev->lock.hw_lock) {
+		dev->lock.hw_lock    = NULL; /* SHM removed */
+		dev->lock.pid	     = 0;
+		wake_up_interruptible(&dev->lock.lock_queue);
+	}
+	up(&dev->struct_sem);
+
+	return 0;
+}
+
+/* tdfx_init is called via init_module at module load time, or via
+ * linux/init/main.c (this is not currently supported). */
+
+static int __init tdfx_init(void)
+{
+	int		      retcode;
+	drm_device_t	      *dev = &tdfx_device;
+
+	DRM_DEBUG("\n");
+
+	memset((void *)dev, 0, sizeof(*dev));
+	dev->count_lock	  = SPIN_LOCK_UNLOCKED;
+	sema_init(&dev->struct_sem, 1);
+
+#ifdef MODULE
+	drm_parse_options(tdfx);
+#endif
+
+	if ((retcode = misc_register(&tdfx_misc))) {
+		DRM_ERROR("Cannot register \"%s\"\n", TDFX_NAME);
+		return retcode;
+	}
+	dev->device = MKDEV(MISC_MAJOR, tdfx_misc.minor);
+	dev->name   = TDFX_NAME;
+
+	drm_mem_init();
+	drm_proc_init(dev);
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	dev->agp    = drm_agp_init();
+#endif
+	if((retcode = drm_ctxbitmap_init(dev))) {
+		DRM_ERROR("Cannot allocate memory for context bitmap.\n");
+		drm_proc_cleanup();
+		misc_deregister(&tdfx_misc);
+		tdfx_takedown(dev);
+		return retcode;
+	}
+
+	DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
+		 TDFX_NAME,
+		 TDFX_MAJOR,
+		 TDFX_MINOR,
+		 TDFX_PATCHLEVEL,
+		 TDFX_DATE,
+		 tdfx_misc.minor);
+
+	return 0;
+}
+
+/* tdfx_cleanup is called via cleanup_module at module unload time. */
+
+static void __exit tdfx_cleanup(void)
+{
+	drm_device_t	      *dev = &tdfx_device;
+
+	DRM_DEBUG("\n");
+
+	drm_proc_cleanup();
+	if (misc_deregister(&tdfx_misc)) {
+		DRM_ERROR("Cannot unload module\n");
+	} else {
+		DRM_INFO("Module unloaded\n");
+	}
+	drm_ctxbitmap_cleanup(dev);
+	tdfx_takedown(dev);
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	if (dev->agp) {
+		drm_agp_uninit();
+		drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS);
+		dev->agp = NULL;
+	}
+#endif
+}
+
+module_init(tdfx_init);
+module_exit(tdfx_cleanup);
+
+
+int tdfx_version(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_version_t version;
+	int	      len;
+
+	if (copy_from_user(&version,
+			   (drm_version_t *)arg,
+			   sizeof(version)))
+		return -EFAULT;
+
+#define DRM_COPY(name,value)				     \
+	len = strlen(value);				     \
+	if (len > name##_len) len = name##_len;		     \
+	name##_len = strlen(value);			     \
+	if (len && name) {				     \
+		if (copy_to_user(name, value, len))	     \
+			return -EFAULT;			     \
+	}
+
+	version.version_major	   = TDFX_MAJOR;
+	version.version_minor	   = TDFX_MINOR;
+	version.version_patchlevel = TDFX_PATCHLEVEL;
+
+	DRM_COPY(version.name, TDFX_NAME);
+	DRM_COPY(version.date, TDFX_DATE);
+	DRM_COPY(version.desc, TDFX_DESC);
+
+	if (copy_to_user((drm_version_t *)arg,
+			 &version,
+			 sizeof(version)))
+		return -EFAULT;
+	return 0;
+}
+
+int tdfx_open(struct inode *inode, struct file *filp)
+{
+	drm_device_t  *dev    = &tdfx_device;
+	int	      retcode = 0;
+
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	if (!(retcode = drm_open_helper(inode, filp, dev))) {
+#if LINUX_VERSION_CODE < 0x020333
+		MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+		atomic_inc(&dev->total_open);
+		spin_lock(&dev->count_lock);
+		if (!dev->open_count++) {
+			spin_unlock(&dev->count_lock);
+			return tdfx_setup(dev);
+		}
+		spin_unlock(&dev->count_lock);
+	}
+	return retcode;
+}
+
+int tdfx_release(struct inode *inode, struct file *filp)
+{
+	drm_file_t    *priv   = filp->private_data;
+	drm_device_t  *dev;
+	int	      retcode = 0;
+
+	lock_kernel();
+	dev = priv->dev;
+
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	if (!(retcode = drm_release(inode, filp))) {
+#if LINUX_VERSION_CODE < 0x020333
+		MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+		atomic_inc(&dev->total_close);
+		spin_lock(&dev->count_lock);
+		if (!--dev->open_count) {
+			if (atomic_read(&dev->ioctl_count) || dev->blocked) {
+				DRM_ERROR("Device busy: %d %d\n",
+					  atomic_read(&dev->ioctl_count),
+					  dev->blocked);
+				spin_unlock(&dev->count_lock);
+				unlock_kernel();
+				return -EBUSY;
+			}
+			spin_unlock(&dev->count_lock);
+			unlock_kernel();
+			return tdfx_takedown(dev);
+		}
+		spin_unlock(&dev->count_lock);
+	}
+
+	unlock_kernel();
+	return retcode;
+}
+
+/* tdfx_ioctl is called whenever a process performs an ioctl on /dev/drm. */
+
+int tdfx_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	int		 nr	 = DRM_IOCTL_NR(cmd);
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	int		 retcode = 0;
+	drm_ioctl_desc_t *ioctl;
+	drm_ioctl_t	 *func;
+
+	atomic_inc(&dev->ioctl_count);
+	atomic_inc(&dev->total_ioctl);
+	++priv->ioctl_count;
+
+	DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n",
+		  current->pid, cmd, nr, dev->device, priv->authenticated);
+
+	if (nr >= TDFX_IOCTL_COUNT) {
+		retcode = -EINVAL;
+	} else {
+		ioctl	  = &tdfx_ioctls[nr];
+		func	  = ioctl->func;
+
+		if (!func) {
+			DRM_DEBUG("no function\n");
+			retcode = -EINVAL;
+		} else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN))
+			    || (ioctl->auth_needed && !priv->authenticated)) {
+			retcode = -EACCES;
+		} else {
+			retcode = (func)(inode, filp, cmd, arg);
+		}
+	}
+
+	atomic_dec(&dev->ioctl_count);
+	return retcode;
+}
+
+int tdfx_lock(struct inode *inode, struct file *filp, unsigned int cmd,
+	      unsigned long arg)
+{
+        drm_file_t        *priv   = filp->private_data;
+        drm_device_t      *dev    = priv->dev;
+        DECLARE_WAITQUEUE(entry, current);
+        int               ret   = 0;
+        drm_lock_t        lock;
+#if DRM_DMA_HISTOGRAM
+        cycles_t          start;
+
+        dev->lck_start = start = get_cycles();
+#endif
+
+        if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)))
+		return -EFAULT;
+
+        if (lock.context == DRM_KERNEL_CONTEXT) {
+                DRM_ERROR("Process %d using kernel context %d\n",
+                          current->pid, lock.context);
+                return -EINVAL;
+        }
+
+        DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
+                  lock.context, current->pid, dev->lock.hw_lock->lock,
+                  lock.flags);
+
+#if 0
+				/* dev->queue_count == 0 right now for
+                                   tdfx.  FIXME? */
+        if (lock.context < 0 || lock.context >= dev->queue_count)
+                return -EINVAL;
+#endif
+
+        if (!ret) {
+#if 0
+                if (_DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)
+                    != lock.context) {
+                        long j = jiffies - dev->lock.lock_time;
+
+                        if (lock.context == tdfx_res_ctx.handle &&
+				j >= 0 && j < DRM_LOCK_SLICE) {
+                                /* Can't take lock if we just had it and
+                                   there is contention. */
+                                DRM_DEBUG("%d (pid %d) delayed j=%d dev=%d jiffies=%d\n",
+					lock.context, current->pid, j,
+					dev->lock.lock_time, jiffies);
+                                current->state = TASK_INTERRUPTIBLE;
+				current->policy |= SCHED_YIELD;
+                                schedule_timeout(DRM_LOCK_SLICE-j);
+				DRM_DEBUG("jiffies=%d\n", jiffies);
+                        }
+                }
+#endif
+                add_wait_queue(&dev->lock.lock_queue, &entry);
+                for (;;) {
+                        current->state = TASK_INTERRUPTIBLE;
+                        if (!dev->lock.hw_lock) {
+                                /* Device has been unregistered */
+                                ret = -EINTR;
+                                break;
+                        }
+                        if (drm_lock_take(&dev->lock.hw_lock->lock,
+                                          lock.context)) {
+                                dev->lock.pid       = current->pid;
+                                dev->lock.lock_time = jiffies;
+                                atomic_inc(&dev->total_locks);
+                                break;  /* Got lock */
+                        }
+
+                                /* Contention */
+                        atomic_inc(&dev->total_sleeps);
+#if 1
+			current->policy |= SCHED_YIELD;
+#endif
+                        schedule();
+                        if (signal_pending(current)) {
+                                ret = -ERESTARTSYS;
+                                break;
+                        }
+                }
+                current->state = TASK_RUNNING;
+                remove_wait_queue(&dev->lock.lock_queue, &entry);
+        }
+
+#if 0
+	if (!ret && dev->last_context != lock.context &&
+		lock.context != tdfx_res_ctx.handle &&
+		dev->last_context != tdfx_res_ctx.handle) {
+		add_wait_queue(&dev->context_wait, &entry);
+	        current->state = TASK_INTERRUPTIBLE;
+                /* PRE: dev->last_context != lock.context */
+	        tdfx_context_switch(dev, dev->last_context, lock.context);
+		/* POST: we will wait for the context
+                   switch and will dispatch on a later call
+                   when dev->last_context == lock.context
+                   NOTE WE HOLD THE LOCK THROUGHOUT THIS
+                   TIME! */
+		current->policy |= SCHED_YIELD;
+	        schedule();
+	        current->state = TASK_RUNNING;
+	        remove_wait_queue(&dev->context_wait, &entry);
+	        if (signal_pending(current)) {
+	                ret = -EINTR;
+	        } else if (dev->last_context != lock.context) {
+			DRM_ERROR("Context mismatch: %d %d\n",
+                        	dev->last_context, lock.context);
+	        }
+	}
+#endif
+
+        if (!ret) {
+		sigemptyset(&dev->sigmask);
+		sigaddset(&dev->sigmask, SIGSTOP);
+		sigaddset(&dev->sigmask, SIGTSTP);
+		sigaddset(&dev->sigmask, SIGTTIN);
+		sigaddset(&dev->sigmask, SIGTTOU);
+		dev->sigdata.context = lock.context;
+		dev->sigdata.lock    = dev->lock.hw_lock;
+		block_all_signals(drm_notifier, &dev->sigdata, &dev->sigmask);
+
+                if (lock.flags & _DRM_LOCK_READY) {
+				/* Wait for space in DMA/FIFO */
+		}
+                if (lock.flags & _DRM_LOCK_QUIESCENT) {
+				/* Make hardware quiescent */
+#if 0
+                        tdfx_quiescent(dev);
+#endif
+		}
+        }
+
+#if LINUX_VERSION_CODE < 0x020400
+	if (lock.context != tdfx_res_ctx.handle) {
+		current->counter = 5;
+		current->priority = DEF_PRIORITY/4;
+	}
+#endif
+        DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
+
+#if DRM_DMA_HISTOGRAM
+        atomic_inc(&dev->histo.lacq[drm_histogram_slot(get_cycles() - start)]);
+#endif
+
+        return ret;
+}
+
+
+int tdfx_unlock(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	drm_lock_t	  lock;
+
+	if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)))
+		return -EFAULT;
+
+	if (lock.context == DRM_KERNEL_CONTEXT) {
+		DRM_ERROR("Process %d using kernel context %d\n",
+			  current->pid, lock.context);
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("%d frees lock (%d holds)\n",
+		  lock.context,
+		  _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+	atomic_inc(&dev->total_unlocks);
+	if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock))
+		atomic_inc(&dev->total_contends);
+	drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT);
+				/* FIXME: Try to send data to card here */
+	if (!dev->context_flag) {
+		if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				  DRM_KERNEL_CONTEXT)) {
+			DRM_ERROR("\n");
+		}
+	}
+
+#if LINUX_VERSION_CODE < 0x020400
+	if (lock.context != tdfx_res_ctx.handle) {
+		current->counter = 5;
+		current->priority = DEF_PRIORITY;
+	}
+#endif
+
+	unblock_all_signals();
+	return 0;
+}
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/tdfx_drv.h linux/drivers/char/drm-4.0/tdfx_drv.h
--- linux-2.4.18-pre2/drivers/char/drm-4.0/tdfx_drv.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/tdfx_drv.h	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,67 @@
+/* tdfx_drv.h -- Private header for tdfx driver -*- linux-c -*-
+ * Created: Thu Oct  7 10:40:04 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *    Daryll Strauss <daryll@valinux.com>
+ * 
+ */
+
+#ifndef _TDFX_DRV_H_
+#define _TDFX_DRV_H_
+
+				/* tdfx_drv.c */
+extern int  tdfx_version(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  tdfx_open(struct inode *inode, struct file *filp);
+extern int  tdfx_release(struct inode *inode, struct file *filp);
+extern int  tdfx_ioctl(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  tdfx_lock(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  tdfx_unlock(struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg);
+
+				/* tdfx_context.c */
+
+extern int  tdfx_resctx(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  tdfx_addctx(struct inode *inode, struct file *filp,
+		        unsigned int cmd, unsigned long arg);
+extern int  tdfx_modctx(struct inode *inode, struct file *filp,
+		        unsigned int cmd, unsigned long arg);
+extern int  tdfx_getctx(struct inode *inode, struct file *filp,
+		        unsigned int cmd, unsigned long arg);
+extern int  tdfx_switchctx(struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg);
+extern int  tdfx_newctx(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  tdfx_rmctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+
+extern int  tdfx_context_switch(drm_device_t *dev, int old, int new);
+extern int  tdfx_context_switch_complete(drm_device_t *dev, int new);
+#endif
diff -urN linux-2.4.18-pre2/drivers/char/drm-4.0/vm.c linux/drivers/char/drm-4.0/vm.c
--- linux-2.4.18-pre2/drivers/char/drm-4.0/vm.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/drm-4.0/vm.c	Thu Jan 10 13:28:34 2002
@@ -0,0 +1,370 @@
+/* vm.c -- Memory mapping for DRM -*- linux-c -*-
+ * Created: Mon Jan  4 08:58:31 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+struct vm_operations_struct   drm_vm_ops = {
+	nopage:	 drm_vm_nopage,
+	open:	 drm_vm_open,
+	close:	 drm_vm_close,
+};
+
+struct vm_operations_struct   drm_vm_shm_ops = {
+	nopage:	 drm_vm_shm_nopage,
+	open:	 drm_vm_open,
+	close:	 drm_vm_close,
+};
+
+struct vm_operations_struct   drm_vm_shm_lock_ops = {
+	nopage:	 drm_vm_shm_nopage_lock,
+	open:	 drm_vm_open,
+	close:	 drm_vm_close,
+};
+
+struct vm_operations_struct   drm_vm_dma_ops = {
+	nopage:	 drm_vm_dma_nopage,
+	open:	 drm_vm_open,
+	close:	 drm_vm_close,
+};
+
+#if LINUX_VERSION_CODE < 0x020317
+unsigned long drm_vm_nopage(struct vm_area_struct *vma,
+			    unsigned long address,
+			    int write_access)
+#else
+				/* Return type changed in 2.3.23 */
+struct page *drm_vm_nopage(struct vm_area_struct *vma,
+			   unsigned long address,
+			   int write_access)
+#endif
+{
+	return NOPAGE_SIGBUS;		/* Disallow mremap */
+}
+
+#if LINUX_VERSION_CODE < 0x020317
+unsigned long drm_vm_shm_nopage(struct vm_area_struct *vma,
+				unsigned long address,
+				int write_access)
+#else
+				/* Return type changed in 2.3.23 */
+struct page *drm_vm_shm_nopage(struct vm_area_struct *vma,
+			       unsigned long address,
+			       int write_access)
+#endif
+{
+#if LINUX_VERSION_CODE >= 0x020300
+	drm_map_t	 *map	 = (drm_map_t *)vma->vm_private_data;
+#else
+	drm_map_t	 *map	 = (drm_map_t *)vma->vm_pte;
+#endif
+	unsigned long	 physical;
+	unsigned long	 offset;
+
+	if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */
+	if (!map)    		   return NOPAGE_OOM;  /* Nothing allocated */
+
+	offset	 = address - vma->vm_start;
+	physical = (unsigned long)map->handle + offset;
+	atomic_inc(&virt_to_page(physical)->count); /* Dec. by kernel */
+
+	DRM_DEBUG("0x%08lx => 0x%08lx\n", address, physical);
+#if LINUX_VERSION_CODE < 0x020317
+	return physical;
+#else
+	return virt_to_page(physical);
+#endif
+}
+
+#if LINUX_VERSION_CODE < 0x020317
+unsigned long drm_vm_shm_nopage_lock(struct vm_area_struct *vma,
+				     unsigned long address,
+				     int write_access)
+#else
+				/* Return type changed in 2.3.23 */
+struct page *drm_vm_shm_nopage_lock(struct vm_area_struct *vma,
+				    unsigned long address,
+				    int write_access)
+#endif
+{
+	drm_file_t	 *priv	 = vma->vm_file->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	unsigned long	 physical;
+	unsigned long	 offset;
+	unsigned long	 page;
+
+	if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */
+	if (!dev->lock.hw_lock)    return NOPAGE_OOM;  /* Nothing allocated */
+
+	offset	 = address - vma->vm_start;
+	page	 = offset >> PAGE_SHIFT;
+	physical = (unsigned long)dev->lock.hw_lock + offset;
+	atomic_inc(&virt_to_page(physical)->count); /* Dec. by kernel */
+
+	DRM_DEBUG("0x%08lx (page %lu) => 0x%08lx\n", address, page, physical);
+#if LINUX_VERSION_CODE < 0x020317
+	return physical;
+#else
+	return virt_to_page(physical);
+#endif
+}
+
+#if LINUX_VERSION_CODE < 0x020317
+unsigned long drm_vm_dma_nopage(struct vm_area_struct *vma,
+				unsigned long address,
+				int write_access)
+#else
+				/* Return type changed in 2.3.23 */
+struct page *drm_vm_dma_nopage(struct vm_area_struct *vma,
+			       unsigned long address,
+			       int write_access)
+#endif
+{
+	drm_file_t	 *priv	 = vma->vm_file->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	unsigned long	 physical;
+	unsigned long	 offset;
+	unsigned long	 page;
+
+	if (!dma)		   return NOPAGE_SIGBUS; /* Error */
+	if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */
+	if (!dma->pagelist)	   return NOPAGE_OOM ; /* Nothing allocated */
+
+	offset	 = address - vma->vm_start; /* vm_[pg]off[set] should be 0 */
+	page	 = offset >> PAGE_SHIFT;
+	physical = dma->pagelist[page] + (offset & (~PAGE_MASK));
+	atomic_inc(&virt_to_page(physical)->count); /* Dec. by kernel */
+
+	DRM_DEBUG("0x%08lx (page %lu) => 0x%08lx\n", address, page, physical);
+#if LINUX_VERSION_CODE < 0x020317
+	return physical;
+#else
+	return virt_to_page(physical);
+#endif
+}
+
+void drm_vm_open(struct vm_area_struct *vma)
+{
+	drm_file_t	*priv	= vma->vm_file->private_data;
+	drm_device_t	*dev	= priv->dev;
+#if DRM_DEBUG_CODE
+	drm_vma_entry_t *vma_entry;
+#endif
+
+	DRM_DEBUG("0x%08lx,0x%08lx\n",
+		  vma->vm_start, vma->vm_end - vma->vm_start);
+	atomic_inc(&dev->vma_count);
+#if LINUX_VERSION_CODE < 0x020333
+				/* The map can exist after the fd is closed. */
+	MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+
+
+#if DRM_DEBUG_CODE
+	vma_entry = drm_alloc(sizeof(*vma_entry), DRM_MEM_VMAS);
+	if (vma_entry) {
+		down(&dev->struct_sem);
+		vma_entry->vma	= vma;
+		vma_entry->next = dev->vmalist;
+		vma_entry->pid	= current->pid;
+		dev->vmalist	= vma_entry;
+		up(&dev->struct_sem);
+	}
+#endif
+}
+
+void drm_vm_close(struct vm_area_struct *vma)
+{
+	drm_file_t	*priv	= vma->vm_file->private_data;
+	drm_device_t	*dev	= priv->dev;
+#if DRM_DEBUG_CODE
+	drm_vma_entry_t *pt, *prev;
+#endif
+
+	DRM_DEBUG("0x%08lx,0x%08lx\n",
+		  vma->vm_start, vma->vm_end - vma->vm_start);
+#if LINUX_VERSION_CODE < 0x020333
+	MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+	atomic_dec(&dev->vma_count);
+
+#if DRM_DEBUG_CODE
+	down(&dev->struct_sem);
+	for (pt = dev->vmalist, prev = NULL; pt; prev = pt, pt = pt->next) {
+		if (pt->vma == vma) {
+			if (prev) {
+				prev->next = pt->next;
+			} else {
+				dev->vmalist = pt->next;
+			}
+			drm_free(pt, sizeof(*pt), DRM_MEM_VMAS);
+			break;
+		}
+	}
+	up(&dev->struct_sem);
+#endif
+}
+
+int drm_mmap_dma(struct file *filp, struct vm_area_struct *vma)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev;
+	drm_device_dma_t *dma;
+	unsigned long	 length	 = vma->vm_end - vma->vm_start;
+	
+	lock_kernel();
+	dev	 = priv->dev;
+	dma	 = dev->dma;
+	DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n",
+		  vma->vm_start, vma->vm_end, VM_OFFSET(vma));
+
+				/* Length must match exact page count */
+	if (!dma || (length >> PAGE_SHIFT) != dma->page_count) {
+		unlock_kernel();
+		return -EINVAL;
+	}
+	unlock_kernel();
+
+	vma->vm_ops   = &drm_vm_dma_ops;
+	vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */
+	
+#if LINUX_VERSION_CODE < 0x020203 /* KERNEL_VERSION(2,2,3) */
+				/* In Linux 2.2.3 and above, this is
+				   handled in do_mmap() in mm/mmap.c. */
+	++filp->f_count;
+#endif
+	vma->vm_file  =	 filp;	/* Needed for drm_vm_open() */
+	drm_vm_open(vma);
+	return 0;
+}
+
+int drm_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_map_t	*map	= NULL;
+	int		i;
+	
+	DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n",
+		  vma->vm_start, vma->vm_end, VM_OFFSET(vma));
+
+	if (!VM_OFFSET(vma)) return drm_mmap_dma(filp, vma);
+
+				/* A sequential search of a linked list is
+				   fine here because: 1) there will only be
+				   about 5-10 entries in the list and, 2) a
+				   DRI client only has to do this mapping
+				   once, so it doesn't have to be optimized
+				   for performance, even if the list was a
+				   bit longer. */
+	for (i = 0; i < dev->map_count; i++) {
+		map = dev->maplist[i];
+		if (map->offset == VM_OFFSET(vma)) break;
+	}
+	
+	if (i >= dev->map_count) return -EINVAL;
+	if (!map || ((map->flags&_DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN)))
+		return -EPERM;
+
+				/* Check for valid size. */
+	if (map->size != vma->vm_end - vma->vm_start) return -EINVAL;
+	
+	if (!capable(CAP_SYS_ADMIN) && (map->flags & _DRM_READ_ONLY)) {
+		vma->vm_flags &= VM_MAYWRITE;
+#if defined(__i386__)
+		pgprot_val(vma->vm_page_prot) &= ~_PAGE_RW;
+#else
+				/* Ye gads this is ugly.  With more thought
+                                   we could move this up higher and use
+                                   `protection_map' instead.  */
+		vma->vm_page_prot = __pgprot(pte_val(pte_wrprotect(
+			__pte(pgprot_val(vma->vm_page_prot)))));
+#endif
+	}
+
+	switch (map->type) {
+	case _DRM_FRAME_BUFFER:
+	case _DRM_REGISTERS:
+	case _DRM_AGP:
+		if (VM_OFFSET(vma) >= __pa(high_memory)) {
+#if defined(__i386__) || defined(__x86_64__)
+			if (boot_cpu_data.x86 > 3 && map->type != _DRM_AGP) {
+				pgprot_val(vma->vm_page_prot) |= _PAGE_PCD;
+				pgprot_val(vma->vm_page_prot) &= ~_PAGE_PWT;
+			}
+#elif defined(__ia64__)
+			if (map->type != _DRM_AGP)
+				vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+#endif
+			vma->vm_flags |= VM_IO;	/* not in core dump */
+		}
+		if (remap_page_range(vma->vm_start,
+				     VM_OFFSET(vma),
+				     vma->vm_end - vma->vm_start,
+				     vma->vm_page_prot))
+				return -EAGAIN;
+		DRM_DEBUG("   Type = %d; start = 0x%lx, end = 0x%lx,"
+			  " offset = 0x%lx\n",
+			  map->type,
+			  vma->vm_start, vma->vm_end, VM_OFFSET(vma));
+		vma->vm_ops = &drm_vm_ops;
+		break;
+	case _DRM_SHM:
+		if (map->flags & _DRM_CONTAINS_LOCK)
+			vma->vm_ops = &drm_vm_shm_lock_ops;
+		else {
+			vma->vm_ops = &drm_vm_shm_ops;
+#if LINUX_VERSION_CODE >= 0x020300
+			vma->vm_private_data = (void *)map;
+#else
+			vma->vm_pte = (unsigned long)map;
+#endif
+		}
+
+				/* Don't let this area swap.  Change when
+				   DRM_KERNEL advisory is supported. */
+		vma->vm_flags |= VM_LOCKED;
+		break;
+	default:
+		return -EINVAL;	/* This should never happen. */
+	}
+	vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */
+
+#if LINUX_VERSION_CODE < 0x020203 /* KERNEL_VERSION(2,2,3) */
+				/* In Linux 2.2.3 and above, this is
+				   handled in do_mmap() in mm/mmap.c. */
+	++filp->f_count;
+#endif
+	vma->vm_file  =	 filp;	/* Needed for drm_vm_open() */
+	drm_vm_open(vma);
+	return 0;
+}
diff -urN linux-2.4.18-pre2/drivers/char/keyboard.c linux/drivers/char/keyboard.c
--- linux-2.4.18-pre2/drivers/char/keyboard.c	Tue Sep 18 13:39:51 2001
+++ linux/drivers/char/keyboard.c	Thu Jan 10 13:28:34 2002
@@ -319,7 +319,7 @@
 			compute_shiftstate();
 			kbd->slockstate = 0; /* play it safe */
 #else
-			keysym =  U(plain_map[keycode]);
+			keysym = U(key_maps[0][keycode]);
 			type = KTYP(keysym);
 			if (type == KT_SHIFT)
 			  (*key_handler[type])(keysym & 0xff, up_flag);
@@ -750,7 +750,7 @@
 	    k = i*BITS_PER_LONG;
 	    for(j=0; j<BITS_PER_LONG; j++,k++)
 	      if(test_bit(k, key_down)) {
-		sym = U(plain_map[k]);
+		sym = U(key_maps[0][k]);
 		if(KTYP(sym) == KT_SHIFT || KTYP(sym) == KT_SLOCK) {
 		  val = KVAL(sym);
 		  if (val == KVAL(K_CAPSSHIFT))
diff -urN linux-2.4.18-pre2/drivers/char/ppdev.c linux/drivers/char/ppdev.c
--- linux-2.4.18-pre2/drivers/char/ppdev.c	Thu Jan 10 13:28:30 2002
+++ linux/drivers/char/ppdev.c	Thu Jan 10 13:28:34 2002
@@ -437,8 +437,12 @@
 	    {
 		unsigned int modes;
 
-		modes = pp->pdev->port->modes;
-		if (copy_to_user ((unsigned int *)arg, &modes, sizeof (port->modes))) {
+		port = parport_find_number (minor);
+		if (!port)
+			return -ENODEV;
+
+		modes = port->modes;
+		if (copy_to_user ((unsigned int *)arg, &modes, sizeof (modes))) {
 			return -EFAULT;
 		}
 		return 0;
diff -urN linux-2.4.18-pre2/drivers/i2c/i2c-keywest.c linux/drivers/i2c/i2c-keywest.c
--- linux-2.4.18-pre2/drivers/i2c/i2c-keywest.c	Thu Jan 10 13:28:30 2002
+++ linux/drivers/i2c/i2c-keywest.c	Thu Jan 10 13:28:34 2002
@@ -486,13 +486,12 @@
 	for (bsteps = 0; (steps & 0x01) == 0; bsteps++)
 		steps >>= 1;
 
-	/* For now, we declare 4 channels on uninorth and 1 on keylargo */
 	if (!strcmp(np->parent->name, "uni-n")) {
-		nchan = 4;
+		nchan = 2;
 		addroffset = 3;
 	} else {
-		nchan = 1;
 		addroffset = 0;
+		nchan = 1;
 	}
 
 	tsize = sizeof(struct keywest_iface) +
diff -urN linux-2.4.18-pre2/drivers/ide/ide-cd.c linux/drivers/ide/ide-cd.c
--- linux-2.4.18-pre2/drivers/ide/ide-cd.c	Thu Jan 10 13:28:30 2002
+++ linux/drivers/ide/ide-cd.c	Thu Jan 10 13:28:34 2002
@@ -1462,11 +1462,9 @@
 		ide_init_drive_cmd (&req);
 		req.cmd = PACKET_COMMAND;
 		req.buffer = (char *)pc;
-		if (ide_do_drive_cmd (drive, &req, ide_wait)) {
-			printk("%s: do_drive_cmd returned stat=%02x,err=%02x\n",
-				drive->name, req.buffer[0], req.buffer[1]);
-			/* FIXME: we should probably abort/retry or something */
-		}
+		ide_do_drive_cmd (drive, &req, ide_wait);
+		/* FIXME: we should probably abort/retry or something 
+		 * in case of failure */
 		if (pc->stat != 0) {
 			/* The request failed.  Retry if it was due to a unit
 			   attention status
diff -urN linux-2.4.18-pre2/drivers/ide/ide-pmac.c linux/drivers/ide/ide-pmac.c
--- linux-2.4.18-pre2/drivers/ide/ide-pmac.c	Thu Jan 10 13:28:30 2002
+++ linux/drivers/ide/ide-pmac.c	Thu Jan 10 13:28:34 2002
@@ -385,6 +385,7 @@
 	SELECT_DRIVE(HWIF(drive), drive);
 	SELECT_MASK(HWIF(drive), drive, 0);
 	udelay(1);
+	(void)GET_STAT(); /* Get rid of pending error state */
 	if(wait_for_ready(drive)) {
 		printk(KERN_ERR "pmac_ide_do_setfeature disk not ready before SET_FEATURE!\n");
 		goto out;
diff -urN linux-2.4.18-pre2/drivers/ide/ide.c linux/drivers/ide/ide.c
--- linux-2.4.18-pre2/drivers/ide/ide.c	Thu Jan 10 13:28:30 2002
+++ linux/drivers/ide/ide.c	Thu Jan 10 13:28:34 2002
@@ -1887,7 +1887,6 @@
 		if (drive->part[p].nr_sects > 0) {
 			kdev_t devp = MKDEV(major, minor+p);
 			invalidate_device(devp, 1);
-			set_blocksize(devp, 1024);
 		}
 		drive->part[p].start_sect = 0;
 		drive->part[p].nr_sects   = 0;
diff -urN linux-2.4.18-pre2/drivers/macintosh/via-pmu.c linux/drivers/macintosh/via-pmu.c
--- linux-2.4.18-pre2/drivers/macintosh/via-pmu.c	Thu Jan 10 13:28:30 2002
+++ linux/drivers/macintosh/via-pmu.c	Thu Jan 10 13:28:34 2002
@@ -444,7 +444,7 @@
 			pmu_battery_count = (prim_info[6] >> 16) & 0xff;
 		}
 	}
-#endif /# CONFIG_PMAC_PBOOK */
+#endif /* CONFIG_PMAC_PBOOK */
 	/* Create /proc/pmu */
 	proc_pmu_root = proc_mkdir("pmu", 0);
 	if (proc_pmu_root) {
diff -urN linux-2.4.18-pre2/drivers/media/video/planb.c linux/drivers/media/video/planb.c
--- linux-2.4.18-pre2/drivers/media/video/planb.c	Thu Oct 25 13:53:47 2001
+++ linux/drivers/media/video/planb.c	Thu Jan 10 13:28:34 2002
@@ -90,11 +90,10 @@
 static int planb_open(struct video_device *, int);
 static void planb_close(struct video_device *);
 static int planb_ioctl(struct video_device *, unsigned int, void *);
-static int planb_init_done(struct video_device *);
 static int planb_mmap(struct video_device *, const char *, unsigned long);
 static void planb_irq(int, void *, struct pt_regs *);
 static void release_planb(void);
-int init_planbs(struct video_init *);
+static int init_planbs(void);
 
 /* ------------------ PlanB Internal Functions ------------------ */
 static int planb_prepare_open(struct planb *);
@@ -2079,7 +2078,6 @@
 #endif
 	pb->tab_size = PLANB_MAXLINES + 40;
 	pb->suspend = 0;
-	pb->lock = 0;
 	init_MUTEX(&pb->lock);
 	pb->ch1_cmd = 0;
 	pb->ch2_cmd = 0;
diff -urN linux-2.4.18-pre2/drivers/mtd/nftlmount.c linux/drivers/mtd/nftlmount.c
--- linux-2.4.18-pre2/drivers/mtd/nftlmount.c	Thu Oct  4 15:13:18 2001
+++ linux/drivers/mtd/nftlmount.c	Thu Jan 10 13:28:34 2002
@@ -4,7 +4,7 @@
  * Author: Fabrice Bellard (fabrice.bellard@netgem.com) 
  * Copyright (C) 2000 Netgem S.A.
  *
- * $Id: nftlmount.c,v 1.23 2001/09/19 21:42:32 dwmw2 Exp $
+ * $Id: nftlmount.c,v 1.25 2001/11/30 16:46:27 dwmw2 Exp $
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -39,7 +39,7 @@
 
 #define SECTORSIZE 512
 
-char nftlmountrev[]="$Revision: 1.23 $";
+char nftlmountrev[]="$Revision: 1.25 $";
 
 /* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the
  *	various device information of the NFTL partition and Bad Unit Table. Update
@@ -94,11 +94,11 @@
 			continue;
 		}
 
-#if 1 /* Some people seem to have devices without ECC or erase marks
+#if 0 /* Some people seem to have devices without ECC or erase marks
 	 on the Media Header blocks. There are enough other sanity
 	 checks in here that we can probably do without it.
       */
-		if (le16_to_cpu ((h1.EraseMark | h1.EraseMark1) != ERASE_MARK)) {
+		if (le16_to_cpu(h1.EraseMark | h1.EraseMark1) != ERASE_MARK) {
 			printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but erase mark not present (0x%04x,0x%04x instead)\n",
 			       block * nftl->EraseSize, nftl->mtd->index, 
 			       le16_to_cpu(h1.EraseMark), le16_to_cpu(h1.EraseMark1));
diff -urN linux-2.4.18-pre2/drivers/net/irda/irport.c linux/drivers/net/irda/irport.c
--- linux-2.4.18-pre2/drivers/net/irda/irport.c	Sun Sep 30 12:26:06 2001
+++ linux/drivers/net/irda/irport.c	Thu Jan 10 13:28:34 2002
@@ -140,7 +140,7 @@
 {
 	struct net_device *dev;
 	struct irport_cb *self;
-	int ret;
+	void *ret;
 	int err;
 
 	IRDA_DEBUG(0, __FUNCTION__ "()\n");
@@ -169,13 +169,12 @@
         self->io.fifo_size = 16;
 
 	/* Lock the port that we need */
-	ret = check_region(self->io.sir_base, self->io.sir_ext);
-	if (ret < 0) { 
+	ret = request_region(self->io.sir_base, self->io.sir_ext, driver_name);
+	if (!ret) { 
 		IRDA_DEBUG(0, __FUNCTION__ "(), can't get iobase of 0x%03x\n",
 			   self->io.sir_base);
 		return NULL;
 	}
-	request_region(self->io.sir_base, self->io.sir_ext, driver_name);
 
 	/* Initialize QoS for this device */
 	irda_init_max_qos_capabilies(&self->qos);
diff -urN linux-2.4.18-pre2/drivers/net/irda/nsc-ircc.c linux/drivers/net/irda/nsc-ircc.c
--- linux-2.4.18-pre2/drivers/net/irda/nsc-ircc.c	Thu Oct  4 18:41:09 2001
+++ linux/drivers/net/irda/nsc-ircc.c	Thu Jan 10 13:28:34 2002
@@ -246,7 +246,7 @@
 	struct net_device *dev;
 	struct nsc_ircc_cb *self;
         struct pm_dev *pmdev;
-	int ret;
+	void *ret;
 	int err;
 
 	IRDA_DEBUG(2, __FUNCTION__ "()\n");
@@ -282,15 +282,14 @@
         self->io.fifo_size = 32;
 	
 	/* Reserve the ioports that we need */
-	ret = check_region(self->io.fir_base, self->io.fir_ext);
-	if (ret < 0) { 
+	ret = request_region(self->io.fir_base, self->io.fir_ext, driver_name);
+	if (!ret) {
 		WARNING(__FUNCTION__ "(), can't get iobase of 0x%03x\n",
 			self->io.fir_base);
 		dev_self[i] = NULL;
 		kfree(self);
 		return -ENODEV;
 	}
-	request_region(self->io.fir_base, self->io.fir_ext, driver_name);
 
 	/* Initialize QoS for this device */
 	irda_init_max_qos_capabilies(&self->qos);
diff -urN linux-2.4.18-pre2/drivers/net/irda/w83977af_ir.c linux/drivers/net/irda/w83977af_ir.c
--- linux-2.4.18-pre2/drivers/net/irda/w83977af_ir.c	Sun Sep 30 12:26:06 2001
+++ linux/drivers/net/irda/w83977af_ir.c	Thu Jan 10 13:28:34 2002
@@ -160,7 +160,7 @@
 {
 	struct net_device *dev;
         struct w83977af_ir *self;
-	int ret;
+	void *ret;
 	int err;
 
 	IRDA_DEBUG(0, __FUNCTION__ "()\n");
@@ -190,14 +190,13 @@
         self->io.fifo_size = 32;
 
 	/* Lock the port that we need */
-	ret = check_region(self->io.fir_base, self->io.fir_ext);
-	if (ret < 0) { 
+	ret = request_region(self->io.fir_base, self->io.fir_ext, driver_name);
+	if (!ret) { 
 		IRDA_DEBUG(0, __FUNCTION__ "(), can't get iobase of 0x%03x\n",
 		      self->io.fir_base);
 		/* w83977af_cleanup( self);  */
 		return -ENODEV;
 	}
-	request_region(self->io.fir_base, self->io.fir_ext, driver_name);
 
 	/* Initialize QoS for this device */
 	irda_init_max_qos_capabilies(&self->qos);
diff -urN linux-2.4.18-pre2/drivers/net/mace.c linux/drivers/net/mace.c
--- linux-2.4.18-pre2/drivers/net/mace.c	Thu Jan 10 13:28:30 2002
+++ linux/drivers/net/mace.c	Thu Jan 10 13:28:34 2002
@@ -24,15 +24,18 @@
 static struct net_device *mace_devs;
 static int port_aaui = -1;
 
-#define N_RX_RING	8
-#define N_TX_RING	6
-#define MAX_TX_ACTIVE	1
-#define NCMDS_TX	1	/* dma commands per element in tx ring */
-#define RX_BUFLEN	(ETH_FRAME_LEN + 8)
-#define TX_TIMEOUT	HZ	/* 1 second */
+#define N_RX_RING		8
+#define N_TX_RING		6
+#define MAX_TX_ACTIVE		1
+#define NCMDS_TX		1	/* dma commands per element in tx ring */
+#define RX_BUFLEN		(ETH_FRAME_LEN + 8)
+#define TX_TIMEOUT		HZ	/* 1 second */
+
+/* Chip rev needs workaround on HW & multicast addr change */
+#define BROKEN_ADDRCHG_REV	0x0941
 
 /* Bits in transmit DMA status */
-#define TX_DMA_ERR	0x80
+#define TX_DMA_ERR		0x80
 
 struct mace_data {
     volatile struct mace *mace;
@@ -56,6 +59,7 @@
     struct timer_list tx_timeout;
     int timeout_active;
     int port_aaui;
+    int chipid;
     struct device_node* of_node;
     struct net_device *next_mace;
 };
@@ -177,8 +181,10 @@
 		dev->dev_addr[j] = rev? bitrev(addr[j]): addr[j];
 		printk("%c%.2x", (j? ':': ' '), dev->dev_addr[j]);
 	}
-	printk(", chip revision %d.%d\n",
-		in_8(&mp->mace->chipid_hi), in_8(&mp->mace->chipid_lo));
+	mp->chipid = (in_8(&mp->mace->chipid_hi) << 8) |
+			in_8(&mp->mace->chipid_lo);
+	printk(", chip revision %d.%d\n", mp->chipid >> 8, mp->chipid & 0xff);
+		
 
 	mp = (struct mace_data *) dev->priv;
 	mp->maccc = ENXMT | ENRCV;
@@ -297,10 +303,9 @@
     __mace_set_address(dev, dev->dev_addr);
 
     /* clear the multicast filter */
-    if (in_8(&mb->chipid_hi) == 0x09) {
-	printk("chip 0x09 mcast workaround...\n");
+    if (mp->chipid == BROKEN_ADDRCHG_REV)
 	out_8(&mb->iac, LOGADDR);
-    } else {
+    else {
 	out_8(&mb->iac, ADDRCHG | LOGADDR);
 	while ((in_8(&mb->iac) & ADDRCHG) != 0)
 		;
@@ -309,7 +314,7 @@
 	out_8(&mb->ladrf, 0);
 
     /* done changing address */
-    if (in_8(&mb->chipid_hi) != 0x09)
+    if (mp->chipid != BROKEN_ADDRCHG_REV)
 	out_8(&mb->iac, 0);
 
     if (mp->port_aaui)
@@ -320,21 +325,22 @@
 
 static void __mace_set_address(struct net_device *dev, void *addr)
 {
-    volatile struct mace *mb = ((struct mace_data *) dev->priv)->mace;
+    struct mace_data *mp = (struct mace_data *) dev->priv;
+    volatile struct mace *mb = mp->mace;
     unsigned char *p = addr;
     int i;
 
     /* load up the hardware address */
-    if (in_8(&mb->chipid_hi) == 0x09) {
-	printk("chip 0x09 hwaddr workaround...\n");
+    if (mp->chipid == BROKEN_ADDRCHG_REV)
     	out_8(&mb->iac, PHYADDR);
-    } else {
+    else {
+    	out_8(&mb->iac, ADDRCHG | PHYADDR);
 	while ((in_8(&mb->iac) & ADDRCHG) != 0)
 	    ;
     }
     for (i = 0; i < 6; ++i)
 	out_8(&mb->padr, dev->dev_addr[i] = p[i]);
-    if (in_8(&mb->chipid_hi) != 0x09)
+    if (mp->chipid != BROKEN_ADDRCHG_REV)
         out_8(&mb->iac, 0);
 }
 
@@ -592,18 +598,17 @@
 	printk("\n");
 #endif
 
-	if (in_8(&mb->chipid_hi) == 0x09) {
-	    printk("chip 0x09 mcast workaround...\n");
+	if (mp->chipid == BROKEN_ADDRCHG_REV)
 	    out_8(&mb->iac, LOGADDR);
-	} else {
+	else {
 	    out_8(&mb->iac, ADDRCHG | LOGADDR);
 	    while ((in_8(&mb->iac) & ADDRCHG) != 0)
 		;
 	}
 	for (i = 0; i < 8; ++i)
 	    out_8(&mb->ladrf, multicast_filter[i]);
-	if (in_8(&mb->chipid_hi) != 0x09)
-	    out_8(&mb->iac, LOGADDR);
+	if (mp->chipid != BROKEN_ADDRCHG_REV)
+	    out_8(&mb->iac, 0);
     }
     /* reset maccc */
     out_8(&mb->maccc, mp->maccc);
diff -urN linux-2.4.18-pre2/drivers/net/wavelan.c linux/drivers/net/wavelan.c
--- linux-2.4.18-pre2/drivers/net/wavelan.c	Fri Oct 12 14:21:18 2001
+++ linux/drivers/net/wavelan.c	Thu Jan 10 13:28:35 2002
@@ -4019,7 +4019,8 @@
 
 	dev->irq = irq;
 
-	request_region(ioaddr, sizeof(ha_t), "wavelan");
+	if (!request_region(ioaddr, sizeof(ha_t), "wavelan"))
+		return -EBUSY;
 
 	dev->mem_start = 0x0000;
 	dev->mem_end = 0x0000;
diff -urN linux-2.4.18-pre2/drivers/net/wireless/airport.c linux/drivers/net/wireless/airport.c
--- linux-2.4.18-pre2/drivers/net/wireless/airport.c	Wed Oct 17 14:37:01 2001
+++ linux/drivers/net/wireless/airport.c	Thu Jan 10 13:28:35 2002
@@ -1,4 +1,4 @@
-/* airport.c 0.06f
+/* airport.c 0.09
  *
  * A driver for "Hermes" chipset based Apple Airport wireless
  * card.
@@ -11,6 +11,8 @@
  *  0.06 : fix possible hang on powerup, add sleep support
  */
 
+#include <linux/config.h>
+
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
@@ -33,26 +35,27 @@
 #include <linux/pmu.h>
 
 #include <asm/prom.h>
-#include <asm/feature.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
 #include <asm/irq.h>
 
 #include "hermes.h"
 #include "orinoco.h"
 
-static char version[] __initdata = "airport.c 0.06f (Benjamin Herrenschmidt <benh@kernel.crashing.org>)";
+static char version[] __initdata = "airport.c 0.09 (Benjamin Herrenschmidt <benh@kernel.crashing.org>)";
 MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
 MODULE_DESCRIPTION("Driver for the Apple Airport wireless card.");
 MODULE_LICENSE("Dual MPL/GPL");
 EXPORT_NO_SYMBOLS;
 
-typedef struct dldwd_card {
+struct airport {
 	struct device_node* node;
 	int irq_requested;
 	int ndev_registered;
 	int open;
 	/* Common structure (fully included), see orinoco.h */
-	struct dldwd_priv priv;
-} dldwd_card_t;
+	struct orinoco_private priv;
+};
 
 #ifdef CONFIG_PMAC_PBOOK
 static int airport_sleep_notify(struct pmu_sleep_notifier *self, int when);
@@ -65,8 +68,8 @@
  * Function prototypes
  */
 
-static dldwd_priv_t* airport_attach(struct device_node *of_node);
-static void airport_detach(dldwd_priv_t* priv);
+static struct orinoco_private* airport_attach(struct device_node *of_node);
+static void airport_detach(struct orinoco_private* priv);
 static int airport_init(struct net_device *dev);
 static int airport_open(struct net_device *dev);
 static int airport_stop(struct net_device *dev);
@@ -81,20 +84,15 @@
    device numbers are used to derive the corresponding array index.
 */
 
-static dldwd_priv_t *airport_dev;
+static struct orinoco_private *airport_dev;
 
 static int airport_init(struct net_device *dev)
 {
-	dldwd_priv_t *priv = dev->priv;
 	int rc;
 	
-	TRACE_ENTER(priv->ndev.name);
-
 	MOD_INC_USE_COUNT;
 
-	rc = dldwd_init(dev);
-	if (!rc)
-		priv->hw_ready = 1;
+	rc = orinoco_init(dev);
 
 	MOD_DEC_USE_COUNT;
 
@@ -104,35 +102,33 @@
 static int
 airport_open(struct net_device *dev)
 {
-	dldwd_priv_t *priv = dev->priv;
-	dldwd_card_t* card = (dldwd_card_t *)priv->card;
+	struct orinoco_private *priv = dev->priv;
+	struct airport* card = (struct airport *)priv->card;
 	int rc;
 
-	TRACE_ENTER(priv->ndev.name);
+	netif_device_attach(dev);
 
-	rc = dldwd_reset(priv);
+	rc = orinoco_reset(priv);
 	if (rc)
 		airport_stop(dev);
 	else {
 		card->open = 1;
-		netif_device_attach(dev);
+		netif_start_queue(dev);
 	}
 
-//	TRACE_EXIT(priv->ndev.name);
-
 	return rc;
 }
 
 static int
 airport_stop(struct net_device *dev)
 {
-	dldwd_priv_t *priv = dev->priv;
-	dldwd_card_t* card = (dldwd_card_t *)priv->card;
+	struct orinoco_private *priv = dev->priv;
+	struct airport* card = (struct airport *)priv->card;
 
 	TRACE_ENTER(priv->ndev.name);
 
 	netif_stop_queue(dev);
-	dldwd_shutdown(priv);
+	orinoco_shutdown(priv);
 	card->open = 0;
 
 	TRACE_EXIT(priv->ndev.name);
@@ -144,16 +140,14 @@
 static int
 airport_sleep_notify(struct pmu_sleep_notifier *self, int when)
 {
-	dldwd_priv_t *priv;
-	struct net_device *ndev;
-	dldwd_card_t* card;
+	struct orinoco_private *priv = airport_dev;
+	struct hermes *hw = &priv->hw;
+	struct net_device *dev = &priv->ndev;
+	struct airport* card = (struct airport *)priv->card;
 	int rc;
 	
-	if (!airport_dev)
+	if (! airport_dev)
 		return PBOOK_SLEEP_OK;
-	priv = airport_dev;
-	ndev = &priv->ndev;
-	card = (dldwd_card_t *)priv->card;
 
 	switch (when) {
 	case PBOOK_SLEEP_REQUEST:
@@ -161,41 +155,41 @@
 	case PBOOK_SLEEP_REJECT:
 		break;
 	case PBOOK_SLEEP_NOW:
-		printk(KERN_INFO "%s: Airport entering sleep mode\n", ndev->name);
-		netif_device_detach(ndev);
-		if (card->open)
-			dldwd_shutdown(priv);
-		disable_irq(ndev->irq);
-		feature_set_airport_power(card->node, 0);
-		priv->hw_ready = 0;
+		printk(KERN_INFO "%s: Airport entering sleep mode\n", dev->name);
+		if (card->open) {
+			netif_stop_queue(dev);
+			orinoco_shutdown(priv);
+			netif_device_detach(dev);
+		}
+		disable_irq(dev->irq);
+		pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, card->node, 0, 0);
 		break;
 	case PBOOK_WAKE:
-		printk(KERN_INFO "%s: Airport waking up\n", ndev->name);
-		feature_set_airport_power(card->node, 1);
+		printk(KERN_INFO "%s: Airport waking up\n", dev->name);
+		pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, card->node, 0, 1);
 		mdelay(200);
-		hermes_reset(&priv->hw);
-		priv->hw_ready = 1;		
-		rc = dldwd_reset(priv);
+		hermes_reset(hw);
+		rc = orinoco_reset(priv);
 		if (rc)
 			printk(KERN_ERR "airport: Error %d re-initing card !\n", rc);
 		else if (card->open)
-			netif_device_attach(ndev);
-		enable_irq(ndev->irq);
+			netif_device_attach(dev);
+		enable_irq(dev->irq);
 		break;
 	}
 	return PBOOK_SLEEP_OK;
 }
 #endif /* CONFIG_PMAC_PBOOK */
 
-static dldwd_priv_t*
+static struct orinoco_private*
 airport_attach(struct device_node* of_node)
 {
-	dldwd_priv_t *priv;
+	struct orinoco_private *priv;
 	struct net_device *ndev;
-	dldwd_card_t* card;
+	struct airport* card;
 	hermes_t *hw;
 
-	TRACE_ENTER("dldwd");
+	TRACE_ENTER("orinoco");
 
 	if (of_node->n_addrs < 1 || of_node->n_intrs < 1) {
 		printk(KERN_ERR "airport: wrong interrupt/addresses in OF tree\n");
@@ -216,8 +210,15 @@
 	hw = &priv->hw;
 	card->node = of_node;
 
+	if (!request_OF_resource(of_node, 0, " (airport)")) {
+		printk(KERN_ERR "airport: can't request IO resource !\n");
+		kfree(card);
+		return NULL;
+	}
+	
 	/* Setup the common part */
-	if (dldwd_setup(priv) < 0) {
+	if (orinoco_setup(priv) < 0) {
+		release_OF_resource(of_node, 0);
 		kfree(card);
 		return NULL;
 	}
@@ -234,14 +235,14 @@
 	hermes_struct_init(hw, ndev->base_addr);
 		
 	/* Power up card */
-	feature_set_airport_power(card->node, 1);
+	pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, card->node, 0, 1);
 	current->state = TASK_UNINTERRUPTIBLE;
 	schedule_timeout(HZ);
 
 	/* Reset it before we get the interrupt */
 	hermes_reset(hw);
 
-	if (request_irq(ndev->irq, dldwd_interrupt, 0, "Airport", (void *)priv)) {
+	if (request_irq(ndev->irq, orinoco_interrupt, 0, "Airport", (void *)priv)) {
 		printk(KERN_ERR "airport: Couldn't get IRQ %d\n", ndev->irq);
 		goto failed;
 	}
@@ -260,7 +261,7 @@
 	SET_MODULE_OWNER(ndev);
 
 	/* And give us the proc nodes for debugging */
-	if (dldwd_proc_dev_init(priv) != 0)
+	if (orinoco_proc_dev_init(priv) != 0)
 		printk(KERN_ERR "airport: Failed to create /proc node for %s\n",
 		       ndev->name);
 
@@ -279,14 +280,12 @@
   ======================================================================*/
 
 static void
-airport_detach(dldwd_priv_t *priv)
+airport_detach(struct orinoco_private *priv)
 {
-	dldwd_card_t* card = (dldwd_card_t *)priv->card;
+	struct airport* card = (struct airport *)priv->card;
 
-	priv->hw_ready = 0;
-	
 	/* Unregister proc entry */
-	dldwd_proc_dev_cleanup(priv);
+	orinoco_proc_dev_cleanup(priv);
 
 #ifdef CONFIG_PMAC_PBOOK
 	pmu_unregister_sleep_notifier(&airport_sleep_notifier);
@@ -299,12 +298,13 @@
 		free_irq(priv->ndev.irq, priv);
 	card->irq_requested = 0;
 
-// FIXME
-//	if (ndev->base_addr)
-//		iounmap(ndev->base_addr + _IO_BASE);
-//	ndev->base_addr = 0;
+	if (priv->ndev.base_addr)
+		iounmap((void *)(priv->ndev.base_addr + (unsigned long)_IO_BASE));
+	priv->ndev.base_addr = 0;
+
+	release_OF_resource(card->node, 0);
 	
-	feature_set_airport_power(card->node, 0);
+	pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, card->node, 0, 0);
 	current->state = TASK_UNINTERRUPTIBLE;
 	schedule_timeout(HZ);
 	
diff -urN linux-2.4.18-pre2/drivers/net/wireless/hermes.c linux/drivers/net/wireless/hermes.c
--- linux-2.4.18-pre2/drivers/net/wireless/hermes.c	Tue Oct  9 15:13:03 2001
+++ linux/drivers/net/wireless/hermes.c	Thu Jan 10 13:28:35 2002
@@ -75,9 +75,9 @@
 #include <stdarg.h>
 
 #define DMSG(stuff...) do {printk(KERN_DEBUG "hermes @ 0x%x: " , hw->iobase); \
-			printk(#stuff);} while (0)
+			printk(stuff);} while (0)
 
-#define DEBUG(lvl, stuff...) if ( (lvl) <= HERMES_DEBUG) DMSG(#stuff)
+#define DEBUG(lvl, stuff...) if ( (lvl) <= HERMES_DEBUG) DMSG(stuff)
 
 #else /* ! HERMES_DEBUG */
 
@@ -98,10 +98,17 @@
 */
 static int hermes_issue_cmd(hermes_t *hw, u16 cmd, u16 param0)
 {
+	int k = CMD_BUSY_TIMEOUT;
 	u16 reg;
 
-	/* First check that the command register is not busy */
+	/* First wait for the command register to unbusy */
 	reg = hermes_read_regn(hw, CMD);
+	while ( (reg % HERMES_CMD_BUSY) && k ) {
+		k--;
+		udelay(1);
+		reg = hermes_read_regn(hw, CMD);
+	}
+	DEBUG(3, "hermes_issue_cmd: did %d retries.\n", CMD_BUSY_TIMEOUT-k);
 	if (reg & HERMES_CMD_BUSY) {
 		return -EBUSY;
 	}
@@ -223,8 +230,8 @@
 			       hw->iobase);
 			err = -ENODEV;
 		} else 
-			printk(KERN_ERR "hermes @ 0x%x: CMD register busy in hermes_issue_command().\n",
-			       hw->iobase);
+			printk(KERN_ERR "hermes @ 0x%x: Error %d issuing command.\n",
+			       hw->iobase, err);
 		goto out;
 	}
 
@@ -325,7 +332,7 @@
 
 	k = BAP_BUSY_TIMEOUT;
 	reg = hermes_read_reg(hw, oreg);
-	while ((reg & HERMES_OFFSET_BUSY) & k) {
+	while ((reg & HERMES_OFFSET_BUSY) && k) {
 		k--;
 		udelay(1);
 		reg = hermes_read_reg(hw, oreg);
diff -urN linux-2.4.18-pre2/drivers/net/wireless/hermes.h linux/drivers/net/wireless/hermes.h
--- linux-2.4.18-pre2/drivers/net/wireless/hermes.h	Thu Oct 25 14:01:57 2001
+++ linux/drivers/net/wireless/hermes.h	Thu Jan 10 13:28:35 2002
@@ -138,7 +138,7 @@
 
 /*--- Regulate Commands --------------------------*/
 #define		HERMES_CMD_NOTIFY		(0x0010)
-#define		HERMES_CMD_INQ			(0x0011)
+#define		HERMES_CMD_INQUIRE		(0x0011)
 
 /*--- Configure Commands --------------------------*/
 #define		HERMES_CMD_ACCESS		(0x0021)
@@ -150,88 +150,101 @@
 #define		HERMES_MONITOR_DISABLE		(0x000f)
 
 /*
- * Configuration RIDs
- */
-
-#define		HERMES_RID_CNF_PORTTYPE		(0xfc00)
-#define		HERMES_RID_CNF_MACADDR		(0xfc01)
-#define		HERMES_RID_CNF_DESIRED_SSID	(0xfc02)
-#define		HERMES_RID_CNF_CHANNEL		(0xfc03)
-#define		HERMES_RID_CNF_OWN_SSID		(0xfc04)
-#define		HERMES_RID_CNF_SYSTEM_SCALE	(0xfc06)
-#define		HERMES_RID_CNF_MAX_DATA_LEN	(0xfc07)
-#define		HERMES_RID_CNF_PM_ENABLE	(0xfc09)
-#define		HERMES_RID_CNF_PM_MCAST_RX	(0xfc0b)
-#define		HERMES_RID_CNF_PM_PERIOD	(0xfc0c)
-#define		HERMES_RID_CNF_PM_HOLDOVER	(0xfc0d)
-#define		HERMES_RID_CNF_NICKNAME		(0xfc0e)
-#define		HERMES_RID_CNF_WEP_ON		(0xfc20)
-#define		HERMES_RID_CNF_MWO_ROBUST	(0xfc25)
-#define		HERMES_RID_CNF_MULTICAST_LIST	(0xfc80)
-#define		HERMES_RID_CNF_CREATEIBSS	(0xfc81)
-#define		HERMES_RID_CNF_FRAG_THRESH	(0xfc82)
-#define		HERMES_RID_CNF_RTS_THRESH	(0xfc83)
-#define		HERMES_RID_CNF_TX_RATE_CTRL	(0xfc84)
-#define		HERMES_RID_CNF_PROMISCUOUS	(0xfc85)
-#define		HERMES_RID_CNF_KEYS		(0xfcb0)
-#define		HERMES_RID_CNF_TX_KEY		(0xfcb1)
-#define		HERMES_RID_CNF_TICKTIME		(0xfce0)
-
-#define		HERMES_RID_CNF_INTERSIL_WEP_ON	(0xfc28)
-#define		HERMES_RID_CNF_INTERSIL_TX_KEY	(0xfc23)
-#define		HERMES_RID_CNF_INTERSIL_KEY0	(0xfc24)
-#define		HERMES_RID_CNF_INTERSIL_KEY1	(0xfc25)
-#define		HERMES_RID_CNF_INTERSIL_KEY2	(0xfc26)
-#define		HERMES_RID_CNF_INTERSIL_KEY3	(0xfc27)
-#define		HERMES_RID_CNF_SYMBOL_MANDATORY_BSSID	(0xfc21)
-#define		HERMES_RID_CNF_SYMBOL_AUTH_TYPE		(0xfc2A)
-#define		HERMES_RID_CNF_SYMBOL_BASIC_RATES	(0xfc8A)
-#define		HERMES_RID_CNF_SYMBOL_PREAMBLE		(0xfc8C)
-
-/*
- * Information RIDs
- */
-#define		HERMES_RID_CHANNEL_LIST		(0xfd10)
-#define		HERMES_RID_STAIDENTITY		(0xfd20)
-#define		HERMES_RID_CURRENT_SSID		(0xfd41)
-#define		HERMES_RID_CURRENT_BSSID	(0xfd42)
-#define		HERMES_RID_COMMSQUALITY		(0xfd43)
-#define 	HERMES_RID_CURRENT_TX_RATE	(0xfd44)
-#define 	HERMES_RID_SHORT_RETRY_LIMIT	(0xfd48)
-#define 	HERMES_RID_LONG_RETRY_LIMIT	(0xfd49)
-#define 	HERMES_RID_MAX_TX_LIFETIME	(0xfd4A)
-#define		HERMES_RID_WEP_AVAIL		(0xfd4f)
-#define		HERMES_RID_CURRENT_CHANNEL	(0xfdc1)
-#define		HERMES_RID_DATARATES		(0xfdc6)
-#define		HERMES_RID_SYMBOL_SECONDARY_VER	(0xfd24)
-#define		HERMES_RID_SYMBOL_KEY_LENGTH	(0xfc2B)
-
-/*
  * Frame structures and constants
  */
 
-typedef struct hermes_frame_desc {
-	/* Hermes - i.e. little-endian byte-order */
+struct hermes_rx_descriptor {
 	u16 status;
-	u16 res1, res2;
-	u16 q_info;
-	u16 res3, res4;
-	u16 tx_ctl;
-} __attribute__ ((packed)) hermes_frame_desc_t;
-
-#define		HERMES_RXSTAT_ERR		(0x0003)
-#define		HERMES_RXSTAT_MACPORT		(0x0700)
-#define		HERMES_RXSTAT_MSGTYPE		(0xE000)
-
-#define		HERMES_RXSTAT_BADCRC		(0x0001)
-#define		HERMES_RXSTAT_UNDECRYPTABLE	(0x0002)
-
-/* RFC-1042 encoded frame */
-#define		HERMES_RXSTAT_1042		(0x2000)
-/* Bridge-tunnel encoded frame */
-#define		HERMES_RXSTAT_TUNNEL		(0x4000)
-/* Wavelan-II Management Protocol frame */
-#define		HERMES_RXSTAT_WMP		(0x6000)
+	u32 time;
+	u8 silence;
+	u8 signal;
+	u8 rate;
+	u8 rxflow;
+	u32 reserved;
+} __attribute__ ((packed));
+
+#define HERMES_RXSTAT_ERR		(0x0003)
+#define	HERMES_RXSTAT_BADCRC		(0x0001)
+#define	HERMES_RXSTAT_UNDECRYPTABLE	(0x0002)
+#define	HERMES_RXSTAT_MACPORT		(0x0700)
+#define	HERMES_RXSTAT_MSGTYPE		(0xE000)
+#define	HERMES_RXSTAT_1042		(0x2000)	/* RFC-1042 frame */
+#define	HERMES_RXSTAT_TUNNEL		(0x4000)	/* bridge-tunnel encoded frame */
+#define	HERMES_RXSTAT_WMP		(0x6000)	/* Wavelan-II Management Protocol frame */
+
+struct hermes_tx_descriptor {
+	u16 status;
+	u16 reserved1;
+	u16 reserved2;
+	u32 sw_support;
+	u8 retry_count;
+	u8 tx_rate;
+	u16 tx_control;	
+} __attribute__ ((packed));
+
+#define HERMES_TXSTAT_RETRYERR		(0x0001)
+#define HERMES_TXSTAT_AGEDERR		(0x0002)
+#define HERMES_TXSTAT_DISCON		(0x0004)
+#define HERMES_TXSTAT_FORMERR		(0x0008)
+
+#define HERMES_TXCTRL_TX_OK		(0x0002)	/* ?? interrupt on Tx complete */
+#define HERMES_TXCTRL_TX_EX		(0x0004)	/* ?? interrupt on Tx exception */
+#define HERMES_TXCTRL_802_11		(0x0008)	/* We supply 802.11 header */
+#define HERMES_TXCTRL_ALT_RTRY		(0x0020)
+
+/* Inquiry constants and data types */
+
+#define HERMES_INQ_TALLIES		(0xF100)
+#define HERMES_INQ_SCAN			(0xF101)
+#define HERMES_INQ_LINKSTATUS		(0xF200)
+
+struct hermes_tallies_frame {
+	u16 TxUnicastFrames;
+	u16 TxMulticastFrames;
+	u16 TxFragments;
+	u16 TxUnicastOctets;
+	u16 TxMulticastOctets;
+	u16 TxDeferredTransmissions;
+	u16 TxSingleRetryFrames;
+	u16 TxMultipleRetryFrames;
+	u16 TxRetryLimitExceeded;
+	u16 TxDiscards;
+	u16 RxUnicastFrames;
+	u16 RxMulticastFrames;
+	u16 RxFragments;
+	u16 RxUnicastOctets;
+	u16 RxMulticastOctets;
+	u16 RxFCSErrors;
+	u16 RxDiscards_NoBuffer;
+	u16 TxDiscardsWrongSA;
+	u16 RxWEPUndecryptable;
+	u16 RxMsgInMsgFragments;
+	u16 RxMsgInBadMsgFragments;
+	/* Those last are probably not available in very old firmwares */
+	u16 RxDiscards_WEPICVError;
+	u16 RxDiscards_WEPExcluded;
+} __attribute__ ((packed));
+
+/* Grabbed from wlan-ng - Thanks Mark... - Jean II
+ * This is the result of a scan inquiry command */
+/* Structure describing info about an Access Point */
+struct hermes_scan_apinfo {
+	u16 channel;		/* Channel where the AP sits */
+	u16 noise;		/* Noise level */
+	u16 level;		/* Signal level */
+	u8 bssid[ETH_ALEN];	/* MAC address of the Access Point */
+	u16 beacon_interv;	/* Beacon interval ? */
+	u16 capabilities;	/* Capabilities ? */
+	u8 essid[32];		/* ESSID of the network */
+	u8 rates[10];		/* Bit rate supported */
+	u16 proberesp_rate;	/* ???? */
+} __attribute__ ((packed));
+/* Container */
+struct hermes_scan_frame {
+	u16 rsvd;                   /* ??? */
+	u16 scanreason;             /* ??? */
+	struct hermes_scan_apinfo aps[35];        /* Scan result */
+} __attribute__ ((packed));
 
 #ifdef __KERNEL__
 
@@ -246,16 +259,6 @@
 	u16 status, resp0, resp1, resp2;
 } hermes_response_t;
 
-/* "ID" structure - used for ESSID and station nickname */
-typedef struct hermes_id {
-	u16 len;
-	u16 val[16];
-} __attribute__ ((packed)) hermes_id_t;
-
-typedef struct hermes_multicast {
-	u8 addr[HERMES_MAX_MULTICAST][ETH_ALEN];
-} __attribute__ ((packed)) hermes_multicast_t;
-
 /* Register access convenience macros */
 #define hermes_read_reg(hw, off) (inw((hw)->iobase + (off)))
 #define hermes_write_reg(hw, off, val) (outw_p((val), (hw)->iobase + (off)))
@@ -310,8 +313,17 @@
 {
 	hermes_response_t resp;
 
-	return hermes_docmd_wait(hw, HERMES_CMD_ENABLE | (port << 8), 
+	return hermes_docmd_wait(hw, HERMES_CMD_DISABLE | (port << 8), 
 				 0, &resp);
+}
+
+/* Initiate an INQUIRE command (tallies or scan).  The result will come as an
+ * information frame in __dldwd_ev_info() */
+static inline int hermes_inquire(hermes_t *hw, u16 rid)
+{
+	hermes_response_t resp;
+
+	return hermes_docmd_wait(hw, HERMES_CMD_INQUIRE, rid, &resp);
 }
 
 #define HERMES_BYTES_TO_RECLEN(n) ( ((n) % 2) ? (((n)+1)/2)+1 : ((n)/2)+1 )
diff -urN linux-2.4.18-pre2/drivers/net/wireless/orinoco.c linux/drivers/net/wireless/orinoco.c
--- linux-2.4.18-pre2/drivers/net/wireless/orinoco.c	Tue Oct  9 15:13:03 2001
+++ linux/drivers/net/wireless/orinoco.c	Thu Jan 10 13:28:35 2002
@@ -1,4 +1,4 @@
-/* orinoco.c 0.08a	- (formerly known as dldwd_cs.c and orinoco_cs.c)
+/* orinoco.c 0.09a	- (formerly known as dldwd_cs.c and orinoco_cs.c)
  *
  * A driver for "Hermes" chipset based PCMCIA wireless adaptors, such
  * as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/
@@ -197,18 +197,46 @@
  *	o Fixed bad bug in WEP key handling on Intersil and Symbol firmware,
  *	  which led to an instant crash on big-endian machines.
  *
+ * v0.08a -> v0.08b - 20/11/2001 - David Gibson
+ *	o Lots of cleanup and bugfixes in orinoco_plx.c
+ *	o Cleanup to handling of Tx rate setting.
+ *	o Removed support for old encapsulation method.
+ *	o Removed old "dldwd" names.
+ *	o Split RID constants into a new file hermes_rid.h
+ *	o Renamed RID constants to match linux-wlan-ng and prism2.o
+ *	o Bugfixes in hermes.c
+ *	o Poke the PLX's INTCSR register, so it actually starts
+ *	  generating interrupts.  These cards might actually work now.
+ *	o Update to wireless extensions v12 (Jean II)
+ *	o Support for tallies and inquire command (Jean II)
+ *	o Airport updates for newer PPC kernels (BenH)
+ *
+ * v0.08b -> v0.09 - 21/12/2001 - David Gibson
+ *	o Some new PCI IDs for PLX cards.
+ *	o Removed broken attempt to do ALLMULTI reception.  Just use
+ *	  promiscuous mode instead
+ *	o Preliminary work for list-AP (Jean II)
+ *	o Airport updates from (BenH)
+ *	o Eliminated racy hw_ready stuff
+ *	o Fixed generation of fake events in irq handler.  This should
+ *	  finally kill the EIO problems (Jean II & dgibson)
+ *	o Fixed breakage of bitrate set/get on Agere firmware (Jean II)
+ *
+ * v0.09 -> v0.09a - 2/1/2002 - David Gibson
+ *	o Fixed stupid mistake in multicast list handling, triggering
+ *	  a BUG()
+ *
  * TODO - Jean II
  *	o inline functions (lots of candidate, need to reorder code)
  *	o Test PrismII/Symbol cards & firmware versions
- *	o Mini-PCI support (some people have reported success - JII)
- *	o Find and kill remaining Tx timeout problems
- */
+ *	o Find and kill remaining Tx timeout problems */
 /* Notes on locking:
  *
  * The basic principle of operation is that everything except the
  * interrupt handler is serialized through a single spinlock in the
- * dldwd_priv_t structure, using dldwd_lock() and
- * dldwd_unlock() (which in turn use spin_lock_bh() and spin_unlock_bh()).
+ * struct orinoco_private structure, using dldwd_lock() and
+ * dldwd_unlock() (which in turn use spin_lock_bh() and
+ * spin_unlock_bh()).
  *
  * The kernel's IRQ handling stuff ensures that the interrupt handler
  * does not re-enter itself. The interrupt handler is written such
@@ -260,23 +288,44 @@
 #include <pcmcia/bus_ops.h>
 
 #include "hermes.h"
+#include "hermes_rid.h"
 #include "orinoco.h"
+#include "ieee802_11.h"
 
-static char version[] __initdata = "orinoco.c 0.08a (David Gibson <hermes@gibson.dropbear.id.au> and others)";
+/* Wireless extensions backwares compatibility */
+#ifndef SIOCIWFIRSTPRIV
+#define SIOCIWFIRSTPRIV		SIOCDEVPRIVATE
+#endif /* SIOCIWFIRSTPRIV */
+
+static char version[] __initdata = "orinoco.c 0.09a (David Gibson <hermes@gibson.dropbear.id.au> and others)";
 MODULE_AUTHOR("David Gibson <hermes@gibson.dropbear.id.au>");
 MODULE_DESCRIPTION("Driver for Lucent Orinoco, Prism II based and similar wireless cards");
 MODULE_LICENSE("Dual MPL/GPL");
 
 /* Level of debugging. Used in the macros in orinoco.h */
 #ifdef ORINOCO_DEBUG
-int dldwd_debug = ORINOCO_DEBUG;
-MODULE_PARM(dldwd_debug, "i");
+int orinoco_debug = ORINOCO_DEBUG;
+MODULE_PARM(orinoco_debug, "i");
 #endif
 
-int use_old_encaps = 0;
-MODULE_PARM(use_old_encaps, "i");
+#define ORINOCO_MIN_MTU		256
+#define ORINOCO_MAX_MTU		(HERMES_FRAME_LEN_MAX - ENCAPS_OVERHEAD)
 
 #define SYMBOL_MAX_VER_LEN	(14)
+#define LTV_BUF_SIZE		128
+#define USER_BAP		0
+#define IRQ_BAP			1
+#define ORINOCO_MACPORT		0
+#define MAX_IRQLOOPS_PER_IRQ	10
+#define MAX_IRQLOOPS_PER_JIFFY	(20000/HZ)	/* Based on a guestimate of how many events the
+						   device can legitimately generate */
+#define TX_NICBUF_SIZE		2048
+#define TX_NICBUF_SIZE_BUG	1585		/* Bug in Symbol firmware */
+#define LARGE_KEY_SIZE		13
+#define SMALL_KEY_SIZE		5
+#define MAX_FRAME_SIZE		2304
+
+#define DUMMY_FID		0xFFFF
 
 const long channel_frequency[] = {
 	2412, 2417, 2422, 2427, 2432, 2437, 2442,
@@ -287,37 +336,23 @@
 
 /* This tables gives the actual meanings of the bitrate IDs returned by the firmware.
    It gives the rate in halfMb/s, negative indicates auto mode */
-const int rate_list[] = { 0, 2, 4, -22, 11, 22, -4, -11, 0, 0, 0, 0};
-
-#define NUM_RATES (sizeof(rate_list) / sizeof(rate_list[0]))
-
-struct p80211_hdr {
-	u16 frame_ctl;
-	u16 duration_id;
-	u8 addr1[ETH_ALEN];
-	u8 addr2[ETH_ALEN];
-	u8 addr3[ETH_ALEN];
-	u16 seq_ctl;
-	u8 addr4[ETH_ALEN];
-	u16 data_len;
-} __attribute__ ((packed));
+struct {
+	int bitrate; /* in 100s of kilbits */
+	int automatic;
+	u16 agere_txratectrl;
+	u16 intersil_txratectrl;
+} bitrate_table[] = {
+	{110, 1,  3, 15}, /* Entry 0 is the default */
+	{10,  0,  1,  1},
+	{10,  1,  1,  1},
+	{20,  0,  2,  2},
+	{20,  1,  6,  3},
+	{55, 0,  4,  4},
+	{55, 1,  7,  7},
+	{110, 0,  5,  8},
+};
 
-/* Frame control field constants */
-#define DLDWD_FCTL_VERS			0x0002
-#define DLDWD_FCTL_FTYPE		0x000c
-#define DLDWD_FCTL_STYPE		0x00f0
-#define DLDWD_FCTL_TODS			0x0100
-#define DLDWD_FCTL_FROMDS		0x0200
-#define DLDWD_FCTL_MOREFRAGS		0x0400
-#define DLDWD_FCTL_RETRY		0x0800
-#define DLDWD_FCTL_PM			0x1000
-#define DLDWD_FCTL_MOREDATA		0x2000
-#define DLDWD_FCTL_WEP			0x4000
-#define DLDWD_FCTL_ORDER		0x8000
-
-#define DLDWD_FTYPE_MGMT		0x0000
-#define DLDWD_FTYPE_CTL			0x0004
-#define DLDWD_FTYPE_DATA		0x0008
+#define BITRATE_TABLE_SIZE (sizeof(bitrate_table) / sizeof(bitrate_table[0]))
 
 struct p8022_hdr {
 	u8 dsap;
@@ -326,16 +361,24 @@
 	u8 oui[3];
 } __attribute__ ((packed));
 
-struct dldwd_frame_hdr {
-	hermes_frame_desc_t desc;
-	struct p80211_hdr p80211;
+struct orinoco_rxframe_hdr {
+	struct hermes_rx_descriptor desc;
+	struct ieee802_11_hdr p80211;
+	struct ethhdr p8023;
+	struct p8022_hdr p8022;
+	u16 ethertype;
+} __attribute__ ((packed));
+
+struct orinoco_txframe_hdr {
+	struct hermes_tx_descriptor desc;
+	struct ieee802_11_hdr p80211;
 	struct ethhdr p8023;
 	struct p8022_hdr p8022;
 	u16 ethertype;
 } __attribute__ ((packed));
 
-#define P8023_OFFSET		(sizeof(hermes_frame_desc_t) + \
-				sizeof(struct p80211_hdr))
+#define P8023_OFFSET		(sizeof(struct hermes_rx_descriptor) + \
+				sizeof(struct ieee802_11_hdr))
 #define ENCAPS_OVERHEAD		(sizeof(struct p8022_hdr) + 2)
 
 /* 802.2 LLL header SNAP used for SNAP encapsulation over 802.11 */
@@ -343,114 +386,112 @@
 	0xaa, 0xaa, 0x03, {0x00, 0x00, 0x00}
 };
 
-struct p8022_hdr old_encaps_hdr = {
-	0xaa, 0xaa, 0x03, {0x00, 0x00, 0xf8}
-};
-
 /* How many times to retry if we get an EIO reading the BAP in the Rx path */
 #define RX_EIO_RETRY		10
 
-typedef struct dldwd_commsqual {
+typedef struct orinoco_commsqual {
 	u16 qual, signal, noise;
-} __attribute__ ((packed)) dldwd_commsqual_t;
+} __attribute__ ((packed)) orinoco_commsqual_t;
 
 /*
  * Function prototypes
  */
 
-static void dldwd_stat_gather(struct net_device *dev,
+static void orinoco_stat_gather(struct net_device *dev,
 			      struct sk_buff *skb,
-			      struct dldwd_frame_hdr *hdr);
+			      struct orinoco_rxframe_hdr *hdr);
 
-static struct net_device_stats *dldwd_get_stats(struct net_device *dev);
-static struct iw_statistics *dldwd_get_wireless_stats(struct net_device *dev);
+static struct net_device_stats *orinoco_get_stats(struct net_device *dev);
+static struct iw_statistics *orinoco_get_wireless_stats(struct net_device *dev);
 
 /* Hardware control routines */
 
-static int __dldwd_hw_reset(dldwd_priv_t *priv);
-static int __dldwd_hw_setup_wep(dldwd_priv_t *priv);
-static int dldwd_hw_get_bssid(dldwd_priv_t *priv, char buf[ETH_ALEN]);
-static int dldwd_hw_get_essid(dldwd_priv_t *priv, int *active, char buf[IW_ESSID_MAX_SIZE+1]);
-static long dldwd_hw_get_freq(dldwd_priv_t *priv);
-static int dldwd_hw_get_bitratelist(dldwd_priv_t *priv, int *numrates,
-				    s32 *rates, int max);
+static int __orinoco_hw_reset(struct orinoco_private *priv);
+static int __orinoco_hw_set_bitrate(struct orinoco_private *priv);
+static int __orinoco_hw_setup_wep(struct orinoco_private *priv);
+static int orinoco_hw_get_bssid(struct orinoco_private *priv, char buf[ETH_ALEN]);
+static int orinoco_hw_get_essid(struct orinoco_private *priv, int *active,
+			      char buf[IW_ESSID_MAX_SIZE+1]);
+static long orinoco_hw_get_freq(struct orinoco_private *priv);
+static int orinoco_hw_get_bitratelist(struct orinoco_private *priv, int *numrates,
+				    int32_t *rates, int max);
 
 /* Interrupt handling routines */
-static void __dldwd_ev_tick(dldwd_priv_t *priv, hermes_t *hw);
-static void __dldwd_ev_wterr(dldwd_priv_t *priv, hermes_t *hw);
-static void __dldwd_ev_infdrop(dldwd_priv_t *priv, hermes_t *hw);
-static void __dldwd_ev_info(dldwd_priv_t *priv, hermes_t *hw);
-static void __dldwd_ev_rx(dldwd_priv_t *priv, hermes_t *hw);
-static void __dldwd_ev_txexc(dldwd_priv_t *priv, hermes_t *hw);
-static void __dldwd_ev_tx(dldwd_priv_t *priv, hermes_t *hw);
-static void __dldwd_ev_alloc(dldwd_priv_t *priv, hermes_t *hw);
-
-static int dldwd_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq);
-static int dldwd_ioctl_setiwencode(struct net_device *dev, struct iw_point *erq);
-static int dldwd_ioctl_getiwencode(struct net_device *dev, struct iw_point *erq);
-static int dldwd_ioctl_setessid(struct net_device *dev, struct iw_point *erq);
-static int dldwd_ioctl_getessid(struct net_device *dev, struct iw_point *erq);
-static int dldwd_ioctl_setnick(struct net_device *dev, struct iw_point *nrq);
-static int dldwd_ioctl_getnick(struct net_device *dev, struct iw_point *nrq);
-static int dldwd_ioctl_setfreq(struct net_device *dev, struct iw_freq *frq);
-static int dldwd_ioctl_getsens(struct net_device *dev, struct iw_param *srq);
-static int dldwd_ioctl_setsens(struct net_device *dev, struct iw_param *srq);
-static int dldwd_ioctl_setrts(struct net_device *dev, struct iw_param *rrq);
-static int dldwd_ioctl_setfrag(struct net_device *dev, struct iw_param *frq);
-static int dldwd_ioctl_getfrag(struct net_device *dev, struct iw_param *frq);
-static int dldwd_ioctl_setrate(struct net_device *dev, struct iw_param *frq);
-static int dldwd_ioctl_getrate(struct net_device *dev, struct iw_param *frq);
-static int dldwd_ioctl_setpower(struct net_device *dev, struct iw_param *prq);
-static int dldwd_ioctl_getpower(struct net_device *dev, struct iw_param *prq);
-static int dldwd_ioctl_setport3(struct net_device *dev, struct iwreq *wrq);
-static int dldwd_ioctl_getport3(struct net_device *dev, struct iwreq *wrq);
-static void __dldwd_set_multicast_list(struct net_device *dev);
+static void __orinoco_ev_tick(struct orinoco_private *priv, hermes_t *hw);
+static void __orinoco_ev_wterr(struct orinoco_private *priv, hermes_t *hw);
+static void __orinoco_ev_infdrop(struct orinoco_private *priv, hermes_t *hw);
+static void __orinoco_ev_info(struct orinoco_private *priv, hermes_t *hw);
+static void __orinoco_ev_rx(struct orinoco_private *priv, hermes_t *hw);
+static void __orinoco_ev_txexc(struct orinoco_private *priv, hermes_t *hw);
+static void __orinoco_ev_tx(struct orinoco_private *priv, hermes_t *hw);
+static void __orinoco_ev_alloc(struct orinoco_private *priv, hermes_t *hw);
+
+static int orinoco_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq);
+static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *erq);
+static int orinoco_ioctl_getiwencode(struct net_device *dev, struct iw_point *erq);
+static int orinoco_ioctl_setessid(struct net_device *dev, struct iw_point *erq);
+static int orinoco_ioctl_getessid(struct net_device *dev, struct iw_point *erq);
+static int orinoco_ioctl_setnick(struct net_device *dev, struct iw_point *nrq);
+static int orinoco_ioctl_getnick(struct net_device *dev, struct iw_point *nrq);
+static int orinoco_ioctl_setfreq(struct net_device *dev, struct iw_freq *frq);
+static int orinoco_ioctl_getsens(struct net_device *dev, struct iw_param *srq);
+static int orinoco_ioctl_setsens(struct net_device *dev, struct iw_param *srq);
+static int orinoco_ioctl_setrts(struct net_device *dev, struct iw_param *rrq);
+static int orinoco_ioctl_setfrag(struct net_device *dev, struct iw_param *frq);
+static int orinoco_ioctl_getfrag(struct net_device *dev, struct iw_param *frq);
+static int orinoco_ioctl_setrate(struct net_device *dev, struct iw_param *frq);
+static int orinoco_ioctl_getrate(struct net_device *dev, struct iw_param *frq);
+static int orinoco_ioctl_setpower(struct net_device *dev, struct iw_param *prq);
+static int orinoco_ioctl_getpower(struct net_device *dev, struct iw_param *prq);
+static int orinoco_ioctl_setport3(struct net_device *dev, struct iwreq *wrq);
+static int orinoco_ioctl_getport3(struct net_device *dev, struct iwreq *wrq);
+static void __orinoco_set_multicast_list(struct net_device *dev);
 
 /* /proc debugging stuff */
-static int dldwd_proc_init(void);
-static void dldwd_proc_cleanup(void);
+static int orinoco_proc_init(void);
+static void orinoco_proc_cleanup(void);
 
 /*
  * Inline functions
  */
 static inline void
-dldwd_lock(dldwd_priv_t *priv)
+orinoco_lock(struct orinoco_private *priv)
 {
 	spin_lock_bh(&priv->lock);
 }
 
 static inline void
-dldwd_unlock(dldwd_priv_t *priv)
+orinoco_unlock(struct orinoco_private *priv)
 {
 	spin_unlock_bh(&priv->lock);
 }
 
 static inline int
-dldwd_irqs_allowed(dldwd_priv_t *priv)
+orinoco_irqs_allowed(struct orinoco_private *priv)
 {
-	return test_bit(DLDWD_STATE_DOIRQ, &priv->state);
+	return test_bit(ORINOCO_STATE_DOIRQ, &priv->state);
 }
 
 static inline void
-__dldwd_stop_irqs(dldwd_priv_t *priv)
+__orinoco_stop_irqs(struct orinoco_private *priv)
 {
 	hermes_t *hw = &priv->hw;
 
 	hermes_set_irqmask(hw, 0);
-	clear_bit(DLDWD_STATE_DOIRQ, &priv->state);
-	while (test_bit(DLDWD_STATE_INIRQ, &priv->state))
+	clear_bit(ORINOCO_STATE_DOIRQ, &priv->state);
+	while (test_bit(ORINOCO_STATE_INIRQ, &priv->state))
 		;
 }
 
 static inline void
-__dldwd_start_irqs(dldwd_priv_t *priv, u16 irqmask)
+__orinoco_start_irqs(struct orinoco_private *priv, u16 irqmask)
 {
 	hermes_t *hw = &priv->hw;
 
 	TRACE_ENTER(priv->ndev.name);
 
 	__cli();
-	set_bit(DLDWD_STATE_DOIRQ, &priv->state);
+	set_bit(ORINOCO_STATE_DOIRQ, &priv->state);
 	hermes_set_irqmask(hw, irqmask);
 	__sti();
 
@@ -458,7 +499,7 @@
 }
 
 static inline void
-set_port_type(dldwd_priv_t *priv)
+set_port_type(struct orinoco_private *priv)
 {
 	switch (priv->iw_mode) {
 	case IW_MODE_INFRA:
@@ -481,13 +522,13 @@
 }
 
 extern void
-dldwd_set_multicast_list(struct net_device *dev)
+orinoco_set_multicast_list(struct net_device *dev)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 
-	dldwd_lock(priv);
-	__dldwd_set_multicast_list(dev);
-	dldwd_unlock(priv);
+	orinoco_lock(priv);
+	__orinoco_set_multicast_list(dev);
+	orinoco_unlock(priv);
 }
 
 /*
@@ -495,63 +536,54 @@
  */
 
 static int
-__dldwd_hw_reset(dldwd_priv_t *priv)
+__orinoco_hw_reset(struct orinoco_private *priv)
 {
 	hermes_t *hw = &priv->hw;
-	int err;
 
-	if (! priv->broken_reset)
-		return hermes_reset(hw);
-	else {
-		hw->inten = 0;
-		hermes_write_regn(hw, INTEN, 0);
-		err = hermes_disable_port(hw, 0);
-		hermes_write_regn(hw, EVACK, 0xffff);
-		return err;
-	}
+	return hermes_reset(hw);
 }
 
 void
-dldwd_shutdown(dldwd_priv_t *priv)
+orinoco_shutdown(struct orinoco_private *priv)
 {
 /* 	hermes_t *hw = &priv->hw; */
 	int err = 0;
 
 	TRACE_ENTER(priv->ndev.name);
 
-	dldwd_lock(priv);
-	__dldwd_stop_irqs(priv);
+	orinoco_lock(priv);
+	__orinoco_stop_irqs(priv);
 
-	err = __dldwd_hw_reset(priv);
+	err = __orinoco_hw_reset(priv);
 	if (err && err != -ENODEV) /* If the card is gone, we don't care about shutting it down */
 		printk(KERN_ERR "%s: Error %d shutting down Hermes chipset\n", priv->ndev.name, err);
 
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	TRACE_EXIT(priv->ndev.name);
 }
 
 int
-dldwd_reset(dldwd_priv_t *priv)
+orinoco_reset(struct orinoco_private *priv)
 {
 	struct net_device *dev = &priv->ndev;
 	hermes_t *hw = &priv->hw;
 	int err = 0;
-	hermes_id_t idbuf;
+	struct hermes_idstring idbuf;
 	int frame_size;
 
 	TRACE_ENTER(priv->ndev.name);
 
 	/* Stop other people bothering us */
-	dldwd_lock(priv);
-	__dldwd_stop_irqs(priv);
+	orinoco_lock(priv);
+	__orinoco_stop_irqs(priv);
 
 	/* Check if we need a card reset */
 	if((priv->need_card_reset) && (priv->card_reset_handler != NULL))
 		priv->card_reset_handler(priv);
 
 	/* Do standard firmware reset if we can */
-	err = __dldwd_hw_reset(priv);
+	err = __orinoco_hw_reset(priv);
 	if (err)
 		goto out;
 
@@ -568,11 +600,11 @@
 	
 	/* Set up the link mode */
 	
-	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_PORTTYPE, priv->port_type);
+	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPORTTYPE, priv->port_type);
 	if (err)
 		goto out;
 	if (priv->has_ibss) {
-		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_CREATEIBSS,
+		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFCREATEIBSS,
 					   priv->allow_ibss);
 		if (err)
 			goto out;
@@ -588,7 +620,7 @@
 
 	/* Set up encryption */
 	if (priv->has_wep) {
-		err = __dldwd_hw_setup_wep(priv);
+		err = __orinoco_hw_setup_wep(priv);
 		if (err) {
 			printk(KERN_ERR "%s: Error %d activating WEP.\n",
 			       dev->name, err);
@@ -600,7 +632,7 @@
 	idbuf.len = cpu_to_le16(strlen(priv->desired_essid));
 	memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val));
 	err = hermes_write_ltv(hw, USER_BAP, (priv->port_type == 3) ?
-			       HERMES_RID_CNF_OWN_SSID : HERMES_RID_CNF_DESIRED_SSID,
+			       HERMES_RID_CNFOWNSSID : HERMES_RID_CNFDESIREDSSID,
 			       HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2),
 			       &idbuf);
 	if (err)
@@ -609,58 +641,62 @@
 	/* Set the station name */
 	idbuf.len = cpu_to_le16(strlen(priv->nick));
 	memcpy(&idbuf.val, priv->nick, sizeof(idbuf.val));
-	err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNF_NICKNAME,
+	err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
 			       HERMES_BYTES_TO_RECLEN(strlen(priv->nick)+2),
 			       &idbuf);
 	if (err)
 		goto out;
 
 	/* Set the channel/frequency */
-	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_CHANNEL, priv->channel);
+	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFOWNCHANNEL, priv->channel);
 	if (err)
 		goto out;
 
 	/* Set AP density */
-	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_SYSTEM_SCALE, priv->ap_density);
+	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE, priv->ap_density);
 	if (err)
 		goto out;
 
 	/* Set RTS threshold */
-	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_RTS_THRESH, priv->rts_thresh);
+	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD, priv->rts_thresh);
 	if (err)
 		goto out;
 
 	/* Set fragmentation threshold or MWO robustness */
 	if (priv->has_mwo)
 		err = hermes_write_wordrec(hw, USER_BAP,
-					   HERMES_RID_CNF_MWO_ROBUST, priv->mwo_robust);
+					   HERMES_RID_CNFMWOROBUST_AGERE,
+					   priv->mwo_robust);
 	else
 		err = hermes_write_wordrec(hw, USER_BAP,
-					   HERMES_RID_CNF_FRAG_THRESH, priv->frag_thresh);
+					   HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
+					   priv->frag_thresh);
 	if (err)
 		goto out;
 
 	/* Set bitrate */
-	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_TX_RATE_CTRL,
-				   priv->tx_rate_ctrl);
+	err = __orinoco_hw_set_bitrate(priv);
 	if (err)
 		goto out;
 
 	/* Set power management */
 	if (priv->has_pm) {
-		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_PM_ENABLE,
+		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPMENABLED,
 					   priv->pm_on);
 		if (err)
 			goto out;
-		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_PM_MCAST_RX,
+		err = hermes_write_wordrec(hw, USER_BAP,
+					   HERMES_RID_CNFMULTICASTRECEIVE,
 					   priv->pm_mcast);
 		if (err)
 			goto out;
-		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_PM_PERIOD,
+		err = hermes_write_wordrec(hw, USER_BAP,
+					   HERMES_RID_CNFMAXSLEEPDURATION,
 					   priv->pm_period);
 		if (err)
 			goto out;
-		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_PM_HOLDOVER,
+		err = hermes_write_wordrec(hw, USER_BAP,
+					   HERMES_RID_CNFPMHOLDOVERDURATION,
 					   priv->pm_timeout);
 		if (err)
 			goto out;
@@ -668,7 +704,8 @@
 
 	/* Set preamble - only for Symbol so far... */
 	if (priv->has_preamble) {
-		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_SYMBOL_PREAMBLE,
+		err = hermes_write_wordrec(hw, USER_BAP,
+					   HERMES_RID_CNFPREAMBLE_SYMBOL,
 					   priv->preamble);
 		if (err) {
 			printk(KERN_WARNING "%s: Can't set preamble!\n", dev->name);
@@ -678,28 +715,62 @@
 
 	/* Set promiscuity / multicast*/
 	priv->promiscuous = 0;
-	priv->allmulti = 0;
 	priv->mc_count = 0;
-	__dldwd_set_multicast_list(dev);
-	
-	err = hermes_enable_port(hw, DLDWD_MACPORT);
-	if (err)
-		goto out;
+	__orinoco_set_multicast_list(dev);
 	
-	__dldwd_start_irqs(priv, HERMES_EV_RX | HERMES_EV_ALLOC |
+	__orinoco_start_irqs(priv, HERMES_EV_RX | HERMES_EV_ALLOC |
 			   HERMES_EV_TX | HERMES_EV_TXEXC |
 			   HERMES_EV_WTERR | HERMES_EV_INFO |
 			   HERMES_EV_INFDROP);
 
+	err = hermes_enable_port(hw, ORINOCO_MACPORT);
+	if (err)
+		goto out;
+
  out:
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	TRACE_EXIT(priv->ndev.name);
 
 	return err;
 }
 
-static int __dldwd_hw_setup_wep(dldwd_priv_t *priv)
+static int __orinoco_hw_set_bitrate(struct orinoco_private *priv)
+{
+	hermes_t *hw = &priv->hw;
+	int err = 0;
+
+	TRACE_ENTER(priv->ndev.name);
+
+	if (priv->bitratemode >= BITRATE_TABLE_SIZE) {
+		printk(KERN_ERR "%s: BUG: Invalid bitrate mode %d\n",
+		       priv->ndev.name, priv->bitratemode);
+		return -EINVAL;
+	}
+
+	switch (priv->firmware_type) {
+	case FIRMWARE_TYPE_AGERE:
+		err = hermes_write_wordrec(hw, USER_BAP,
+					   HERMES_RID_CNFTXRATECONTROL,
+					   bitrate_table[priv->bitratemode].agere_txratectrl);
+		break;
+	case FIRMWARE_TYPE_INTERSIL:
+	case FIRMWARE_TYPE_SYMBOL:
+		err = hermes_write_wordrec(hw, USER_BAP,
+					   HERMES_RID_CNFTXRATECONTROL,
+					   bitrate_table[priv->bitratemode].intersil_txratectrl);
+		break;
+	default:
+		BUG();
+	}
+
+	TRACE_EXIT(priv->ndev.name);
+
+	return err;
+}
+
+
+static int __orinoco_hw_setup_wep(struct orinoco_private *priv)
 {
 	hermes_t *hw = &priv->hw;
 	int err = 0;
@@ -709,17 +780,23 @@
 	TRACE_ENTER(priv->ndev.name);
 
 	switch (priv->firmware_type) {
-	case FIRMWARE_TYPE_LUCENT: /* Lucent style WEP */
+	case FIRMWARE_TYPE_AGERE: /* Agere style WEP */
 		if (priv->wep_on) {
-			err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_TX_KEY, priv->tx_key);
+			err = hermes_write_wordrec(hw, USER_BAP,
+						   HERMES_RID_CNFTXKEY_AGERE,
+						   priv->tx_key);
 			if (err)
 				return err;
 			
-			err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNF_KEYS, &priv->keys);
+			err = HERMES_WRITE_RECORD(hw, USER_BAP,
+						  HERMES_RID_CNFWEPKEYS_AGERE,
+						  &priv->keys);
 			if (err)
 				return err;
 		}
-		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_WEP_ON, priv->wep_on);
+		err = hermes_write_wordrec(hw, USER_BAP,
+					   HERMES_RID_CNFWEPENABLED_AGERE,
+					   priv->wep_on);
 		if (err)
 			return err;
 		break;
@@ -728,15 +805,15 @@
 	case FIRMWARE_TYPE_SYMBOL: /* Symbol style WEP */
 		master_wep_flag = 0;		/* Off */
 		if (priv->wep_on) {
-/*  			int keylen; */
+			int keylen;
 			int i;
 
 			/* Fudge around firmware weirdness */
-/*  			keylen = priv->keys[priv->tx_key].len; */
+			keylen = le16_to_cpu(priv->keys[priv->tx_key].len);
 
 			/* Write all 4 keys */
-			for(i = 0; i < MAX_KEYS; i++) {
-				int keylen = le16_to_cpu(priv->keys[i].len);
+			for(i = 0; i < ORINOCO_MAX_KEYS; i++) {
+/*  				int keylen = le16_to_cpu(priv->keys[i].len); */
 
 				if (keylen > LARGE_KEY_SIZE) {
 					printk(KERN_ERR "%s: BUG: Key %d has oversize length %d.\n",
@@ -747,7 +824,7 @@
 				printk("About to write key %d, keylen=%d\n",
 				       i, keylen);				     
 				err = hermes_write_ltv(hw, USER_BAP,
-						       HERMES_RID_CNF_INTERSIL_KEY0 + i,
+						       HERMES_RID_CNFDEFAULTKEY0 + i,
 						       HERMES_BYTES_TO_RECLEN(keylen),
 						       priv->keys[i].data);
 				if (err)
@@ -755,7 +832,7 @@
 			}
 
 			/* Write the index of the key used in transmission */
-			err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_INTERSIL_TX_KEY,
+			err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFWEPDEFAULTKEYID,
 						   priv->tx_key);
 			if (err)
 				return err;
@@ -771,7 +848,8 @@
 					auth_flag = 2;
 				else
 					auth_flag = 1;
-				err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_SYMBOL_AUTH_TYPE, auth_flag);
+				err = hermes_write_wordrec(hw, USER_BAP,
+							   HERMES_RID_CNFAUTHENTICATION, auth_flag);
 				if (err)
 					return err;
 				/* Master WEP setting is always 3 */
@@ -787,7 +865,9 @@
 		}
 		
 		/* Master WEP setting : on/off */
-		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_INTERSIL_WEP_ON, master_wep_flag);
+		err = hermes_write_wordrec(hw, USER_BAP,
+					   HERMES_RID_CNFWEPFLAGS_INTERSIL,
+					   master_wep_flag);
 		if (err)
 			return err;	
 
@@ -806,48 +886,48 @@
 	return 0;
 }
 
-static int dldwd_hw_get_bssid(dldwd_priv_t *priv, char buf[ETH_ALEN])
+static int orinoco_hw_get_bssid(struct orinoco_private *priv, char buf[ETH_ALEN])
 {
 	hermes_t *hw = &priv->hw;
 	int err = 0;
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 
-	err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_BSSID,
+	err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID,
 			      ETH_ALEN, NULL, buf);
 
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	return err;
 }
 
-static int dldwd_hw_get_essid(dldwd_priv_t *priv, int *active,
+static int orinoco_hw_get_essid(struct orinoco_private *priv, int *active,
 			      char buf[IW_ESSID_MAX_SIZE+1])
 {
 	hermes_t *hw = &priv->hw;
 	int err = 0;
-	hermes_id_t essidbuf;
+	struct hermes_idstring essidbuf;
 	char *p = (char *)(&essidbuf.val);
 	int len;
 
 	TRACE_ENTER(priv->ndev.name);
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 
 	if (strlen(priv->desired_essid) > 0) {
 		/* We read the desired SSID from the hardware rather
 		   than from priv->desired_essid, just in case the
 		   firmware is allowed to change it on us. I'm not
 		   sure about this */
-		/* My guess is that the OWN_SSID should always be whatever
+		/* My guess is that the OWNSSID should always be whatever
 		 * we set to the card, whereas CURRENT_SSID is the one that
 		 * may change... - Jean II */
 		u16 rid;
 
 		*active = 1;
 
-		rid = (priv->port_type == 3) ? HERMES_RID_CNF_OWN_SSID :
-			HERMES_RID_CNF_DESIRED_SSID;
+		rid = (priv->port_type == 3) ? HERMES_RID_CNFOWNSSID :
+			HERMES_RID_CNFDESIREDSSID;
 		
 		err = hermes_read_ltv(hw, USER_BAP, rid, sizeof(essidbuf),
 				      NULL, &essidbuf);
@@ -856,7 +936,7 @@
 	} else {
 		*active = 0;
 
-		err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_SSID,
+		err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTSSID,
 				      sizeof(essidbuf), NULL, &essidbuf);
 		if (err)
 			goto fail_unlock;
@@ -869,14 +949,14 @@
 	buf[len] = '\0';
 
  fail_unlock:
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	TRACE_EXIT(priv->ndev.name);
 
 	return err;       
 }
 
-static long dldwd_hw_get_freq(dldwd_priv_t *priv)
+static long orinoco_hw_get_freq(struct orinoco_private *priv)
 {
 	
 	hermes_t *hw = &priv->hw;
@@ -884,9 +964,9 @@
 	u16 channel;
 	long freq = 0;
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 	
-	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENT_CHANNEL, &channel);
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENTCHANNEL, &channel);
 	if (err)
 		goto out;
 
@@ -901,27 +981,27 @@
 	freq = channel_frequency[channel-1] * 100000;
 
  out:
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	if (err > 0)
 		err = -EBUSY;
 	return err ? err : freq;
 }
 
-static int dldwd_hw_get_bitratelist(dldwd_priv_t *priv, int *numrates,
-				    s32 *rates, int max)
+static int orinoco_hw_get_bitratelist(struct orinoco_private *priv, int *numrates,
+				    int32_t *rates, int max)
 {
 	hermes_t *hw = &priv->hw;
-	hermes_id_t list;
+	struct hermes_idstring list;
 	unsigned char *p = (unsigned char *)&list.val;
 	int err = 0;
 	int num;
 	int i;
 
-	dldwd_lock(priv);
-	err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_DATARATES, sizeof(list),
-			      NULL, &list);
-	dldwd_unlock(priv);
+	orinoco_lock(priv);
+	err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES,
+			      sizeof(list), NULL, &list);
+	orinoco_unlock(priv);
 
 	if (err)
 		return err;
@@ -938,18 +1018,18 @@
 }
 
 #ifndef ORINOCO_DEBUG
-static inline void show_rx_frame(struct dldwd_frame_hdr *frame) {}
+static inline void show_rx_frame(struct orinoco_rxframe_hdr *frame) {}
 #else
-static void show_rx_frame(struct dldwd_frame_hdr *frame)
+static void show_rx_frame(struct orinoco_rxframe_hdr *frame)
 {
 	printk(KERN_DEBUG "RX descriptor:\n");
 	printk(KERN_DEBUG "  status      = 0x%04x\n", frame->desc.status);
-	printk(KERN_DEBUG "  res1        = 0x%04x\n", frame->desc.res1);
-	printk(KERN_DEBUG "  res2        = 0x%04x\n", frame->desc.res2);
-	printk(KERN_DEBUG "  q_info      = 0x%04x\n", frame->desc.q_info);
-	printk(KERN_DEBUG "  res3        = 0x%04x\n", frame->desc.res3);
-	printk(KERN_DEBUG "  res4        = 0x%04x\n", frame->desc.res4);
-	printk(KERN_DEBUG "  tx_ctl      = 0x%04x\n", frame->desc.tx_ctl);
+	printk(KERN_DEBUG "  time        = 0x%08x\n", frame->desc.time);
+	printk(KERN_DEBUG "  silence     = 0x%02x\n", frame->desc.silence);
+	printk(KERN_DEBUG "  signal      = 0x%02x\n", frame->desc.signal);
+	printk(KERN_DEBUG "  rate        = 0x%02x\n", frame->desc.rate);
+	printk(KERN_DEBUG "  rxflow      = 0x%02x\n", frame->desc.rxflow);
+	printk(KERN_DEBUG "  reserved    = 0x%08x\n", frame->desc.reserved);
 
 	printk(KERN_DEBUG "IEEE 802.11 header:\n");
 	printk(KERN_DEBUG "  frame_ctl   = 0x%04x\n",
@@ -1001,92 +1081,94 @@
 /*
  * Interrupt handler
  */
-void dldwd_interrupt(int irq, void * dev_id, struct pt_regs *regs)
+void orinoco_interrupt(int irq, void * dev_id, struct pt_regs *regs)
 {
-	dldwd_priv_t *priv = (dldwd_priv_t *) dev_id;
+	struct orinoco_private *priv = (struct orinoco_private *) dev_id;
 	hermes_t *hw = &priv->hw;
 	struct net_device *dev = &priv->ndev;
-	int count = IRQ_LOOP_MAX;
+	int count = MAX_IRQLOOPS_PER_IRQ;
 	u16 evstat, events;
-	static int old_time = 0, timecount = 0; /* Eugh, revolting hack for now */
+	/* These are used to detect a runaway interrupt situation */
+	/* If we get more than MAX_IRQLOOPS_PER_JIFFY iterations in a jiffy,
+	 * we panic and shut down the hardware */
+	static int last_irq_jiffy = 0; /* jiffies value the last time we were called */
+	static int loops_this_jiffy = 0;
 
-	if (test_and_set_bit(DLDWD_STATE_INIRQ, &priv->state))
+	if (test_and_set_bit(ORINOCO_STATE_INIRQ, &priv->state))
 		BUG();
 
-	if (! dldwd_irqs_allowed(priv)) {
-		clear_bit(DLDWD_STATE_INIRQ, &priv->state);
+	if (! orinoco_irqs_allowed(priv)) {
+		clear_bit(ORINOCO_STATE_INIRQ, &priv->state);
 		return;
 	}
 
-	DEBUG(3, "%s: dldwd_interrupt()\n", priv->ndev.name);
+	DEBUG(3, "%s: orinoco_interrupt()\n", priv->ndev.name);
+
+	evstat = hermes_read_regn(hw, EVSTAT);
+	events = evstat & hw->inten;
+	
+	if (! events) { /* Sometimes the card generates Tx interrupts without setting EVSTAT,
+			   or so I've heard - FIXME does it really happen? */
+		printk(KERN_WARNING "%s: NULL event in orinoco_interrupt!\n", priv->ndev.name);
+		__orinoco_ev_alloc(priv, hw);
+	}
 
-	while (1) {
-		if (jiffies != old_time)
-			timecount = 0;
-		if ( (++timecount > 50) || (! count--) ) {
+	if (jiffies != last_irq_jiffy)
+		loops_this_jiffy = 0;
+	last_irq_jiffy = jiffies;
+
+	while (events && count--) {
+		DEBUG(4, "__orinoco_interrupt(): count=%d EVSTAT=0x%04x\n",
+		      count, evstat);
+		
+		if (++loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY) {
 			printk(KERN_CRIT "%s: IRQ handler is looping too \
 much! Shutting down.\n",
 			       dev->name);
 			/* Perform an emergency shutdown */
-			clear_bit(DLDWD_STATE_DOIRQ, &priv->state);
+			clear_bit(ORINOCO_STATE_DOIRQ, &priv->state);
 			hermes_set_irqmask(hw, 0);
 			break;
 		}
 
-		evstat = hermes_read_regn(hw, EVSTAT);
-		DEBUG(3, "__dldwd_interrupt(): count=%d EVSTAT=0x%04x inten=0x%04x\n",
-		      count, evstat, hw->inten);
-
-		events = evstat & hw->inten;
-
-		if (! events) {
-			if (netif_queue_stopped(dev)) {
-				/* There seems to be a firmware bug which
-				   sometimes causes the card to give an
-				   interrupt with no event set, when there
-				   sould be a Tx completed event. */
-				DEBUG(3, "%s: Interrupt with no event (ALLOCFID=0x%04x)\n",
-				      dev->name, (int)hermes_read_regn(hw, ALLOCFID));
-				events = HERMES_EV_TX | HERMES_EV_ALLOC;
-			} else /* Nothing's happening, we're done */
-				break;
-		}
-
 		/* Check the card hasn't been removed */
 		if (! hermes_present(hw)) {
-			DEBUG(0, "dldwd_interrupt(): card removed\n");
+			DEBUG(0, "orinoco_interrupt(): card removed\n");
 			break;
 		}
 
 		if (events & HERMES_EV_TICK)
-			__dldwd_ev_tick(priv, hw);
+			__orinoco_ev_tick(priv, hw);
 		if (events & HERMES_EV_WTERR)
-			__dldwd_ev_wterr(priv, hw);
+			__orinoco_ev_wterr(priv, hw);
 		if (events & HERMES_EV_INFDROP)
-			__dldwd_ev_infdrop(priv, hw);
+			__orinoco_ev_infdrop(priv, hw);
 		if (events & HERMES_EV_INFO)
-			__dldwd_ev_info(priv, hw);
+			__orinoco_ev_info(priv, hw);
 		if (events & HERMES_EV_RX)
-			__dldwd_ev_rx(priv, hw);
+			__orinoco_ev_rx(priv, hw);
 		if (events & HERMES_EV_TXEXC)
-			__dldwd_ev_txexc(priv, hw);
+			__orinoco_ev_txexc(priv, hw);
 		if (events & HERMES_EV_TX)
-			__dldwd_ev_tx(priv, hw);
+			__orinoco_ev_tx(priv, hw);
 		if (events & HERMES_EV_ALLOC)
-			__dldwd_ev_alloc(priv, hw);
+			__orinoco_ev_alloc(priv, hw);
 		
 		hermes_write_regn(hw, EVACK, events);
-	}
 
-	clear_bit(DLDWD_STATE_INIRQ, &priv->state);
+		evstat = hermes_read_regn(hw, EVSTAT);
+		events = evstat & hw->inten;
+	};
+
+	clear_bit(ORINOCO_STATE_INIRQ, &priv->state);
 }
 
-static void __dldwd_ev_tick(dldwd_priv_t *priv, hermes_t *hw)
+static void __orinoco_ev_tick(struct orinoco_private *priv, hermes_t *hw)
 {
 	printk(KERN_DEBUG "%s: TICK\n", priv->ndev.name);
 }
 
-static void __dldwd_ev_wterr(dldwd_priv_t *priv, hermes_t *hw)
+static void __orinoco_ev_wterr(struct orinoco_private *priv, hermes_t *hw)
 {
 	/* This seems to happen a fair bit under load, but ignoring it
 	   seems to work fine...*/
@@ -1094,19 +1176,85 @@
 	      priv->ndev.name);
 }
 
-static void __dldwd_ev_infdrop(dldwd_priv_t *priv, hermes_t *hw)
+static void __orinoco_ev_infdrop(struct orinoco_private *priv, hermes_t *hw)
 {
 	printk(KERN_WARNING "%s: Information frame lost.\n", priv->ndev.name);
 }
 
-static void __dldwd_ev_info(dldwd_priv_t *priv, hermes_t *hw)
+static void __orinoco_ev_info(struct orinoco_private *priv, hermes_t *hw)
 {
-	DEBUG(3, "%s: Information frame received.\n", priv->ndev.name);
-	/* We don't actually do anything about it - we assume the MAC
-	   controller can deal with it */
+	struct net_device *dev = &priv->ndev;
+	u16 infofid;
+	struct {
+		u16 len;
+		u16 type;
+	} __attribute__ ((packed)) info;
+	int err;
+
+	/* This is an answer to an INQUIRE command that we did earlier,
+	 * or an information "event" generated by the card
+	 * The controller return to us a pseudo frame containing
+	 * the information in question - Jean II */
+	infofid = hermes_read_regn(hw, INFOFID);
+	DEBUG(3, "%s: __dldwd_ev_info(): INFOFID=0x%04x\n", dev->name,
+	      infofid);
+
+	/* Read the info frame header - don't try too hard */
+	err = hermes_bap_pread(hw, IRQ_BAP, &info, sizeof(info),
+			       infofid, 0);
+	if (err) {
+		printk(KERN_ERR "%s: error %d reading info frame. "
+		       "Frame dropped.\n", dev->name, err);
+		return;
+	}
+	
+	switch (le16_to_cpu(info.type)) {
+	case HERMES_INQ_TALLIES: {
+		struct hermes_tallies_frame tallies;
+		struct iw_statistics *wstats = &priv->wstats;
+		int len = le16_to_cpu(info.len) - 1;
+		
+		if (len > (sizeof(tallies) / 2)) {
+			DEBUG(1, "%s: tallies frame too long.\n", dev->name);
+			len = sizeof(tallies) / 2;
+		}
+		
+		/* Read directly the data (no seek) */
+		hermes_read_words(hw, HERMES_DATA1, (void *) &tallies, len);
+		
+		/* Increment our various counters */
+		/* wstats->discard.nwid - no wrong BSSID stuff */
+		wstats->discard.code +=
+			le16_to_cpu(tallies.RxWEPUndecryptable);
+		if (len == (sizeof(tallies) / 2))  
+			wstats->discard.code +=
+				le16_to_cpu(tallies.RxDiscards_WEPICVError) +
+				le16_to_cpu(tallies.RxDiscards_WEPExcluded);
+		wstats->discard.misc +=
+			le16_to_cpu(tallies.TxDiscardsWrongSA);
+#if WIRELESS_EXT > 11
+		wstats->discard.fragment +=
+			le16_to_cpu(tallies.RxMsgInBadMsgFragments);
+		wstats->discard.retries +=
+			le16_to_cpu(tallies.TxRetryLimitExceeded);
+		/* wstats->miss.beacon - no match */
+#if ORINOCO_DEBUG > 3
+		/* Hack for debugging - should not be taken as an example */
+		wstats->discard.nwid += le16_to_cpu(tallies.TxUnicastFrames);
+		wstats->miss.beacon += le16_to_cpu(tallies.RxUnicastFrames);
+#endif
+#endif /* WIRELESS_EXT > 11 */
+	}
+	break;
+	default:
+		DEBUG(1, "%s: Unknown information frame received (type %04x).\n",
+		      priv->ndev.name, le16_to_cpu(info.type));
+		/* We don't actually do anything about it */
+		break;
+	}
 }
 
-static void __dldwd_ev_rx(dldwd_priv_t *priv, hermes_t *hw)
+static void __orinoco_ev_rx(struct orinoco_private *priv, hermes_t *hw)
 {
 	struct net_device *dev = &priv->ndev;
 	struct net_device_stats *stats = &priv->stats;
@@ -1116,12 +1264,12 @@
 	u16 rxfid, status;
 	int length, data_len, data_off;
 	char *p;
-	struct dldwd_frame_hdr hdr;
+	struct orinoco_rxframe_hdr hdr;
 	struct ethhdr *eh;
 	int err;
 
 	rxfid = hermes_read_regn(hw, RXFID);
-	DEBUG(3, "__dldwd_ev_rx(): RXFID=0x%04x\n", rxfid);
+	DEBUG(3, "__orinoco_ev_rx(): RXFID=0x%04x\n", rxfid);
 
 	/* We read in the entire frame header here. This isn't really
 	   necessary, since we ignore most of it, but it's
@@ -1140,7 +1288,6 @@
 		stats->rx_errors++;
 		goto drop;
 	}
-	DEBUG(2, "%s: BAP read suceeded: l=%d\n", dev->name, l);
 
 	status = le16_to_cpu(hdr.desc.status);
 	
@@ -1148,7 +1295,6 @@
 		if ((status & HERMES_RXSTAT_ERR) == HERMES_RXSTAT_BADCRC) {
 			stats->rx_crc_errors++;
 			DEBUG(1, "%s: Bad CRC on Rx. Frame dropped.\n", dev->name);
-			show_rx_frame(&hdr);
 		} else if ((status & HERMES_RXSTAT_ERR)
 			   == HERMES_RXSTAT_UNDECRYPTABLE) {
 			wstats->discard.code++;
@@ -1241,7 +1387,6 @@
 		stats->rx_errors++;
 		goto drop;
 	}
-	DEBUG(2, "%s: BAP read suceeded: l=%d\n", dev->name, l);
 
 	dev->last_rx = jiffies;
 	skb->dev = dev;
@@ -1249,7 +1394,7 @@
 	skb->ip_summed = CHECKSUM_NONE;
 	
 	/* Process the wireless stats if needed */
-	dldwd_stat_gather(dev, skb, &hdr);
+	orinoco_stat_gather(dev, skb, &hdr);
 
 	/* Pass the packet to the networking stack */
 	netif_rx(skb);
@@ -1264,45 +1409,60 @@
 	return;
 }
 
-static void __dldwd_ev_txexc(dldwd_priv_t *priv, hermes_t *hw)
+static void __orinoco_ev_txexc(struct orinoco_private *priv, hermes_t *hw)
 {
 	struct net_device *dev = &priv->ndev;
 	struct net_device_stats *stats = &priv->stats;
+	u16 fid = hermes_read_regn(hw, TXCOMPLFID);
 
-	printk(KERN_WARNING "%s: Tx error!\n", dev->name);
+	if (fid != DUMMY_FID) {
+		printk(KERN_INFO "%s: Tx error (FID=%04X)!\n", dev->name, fid);
+		stats->tx_errors++;
+		stats->tx_packets--; /* Reverse the increment done in _ev_alloc */
+	}
 
-	netif_wake_queue(dev);
-	stats->tx_errors++;
+	hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
 }
 
-static void __dldwd_ev_tx(dldwd_priv_t *priv, hermes_t *hw)
+static void __orinoco_ev_tx(struct orinoco_private *priv, hermes_t *hw)
 {
-	struct net_device *dev = &priv->ndev;
-	struct net_device_stats *stats = &priv->stats;
+	/* We don't generally use the Tx event (to cut down on
+	   interrupts) - we do the transmit complet processing once
+	   the transmit buffer is reclaimed in __orinoco_ev_alloc() ,
+	   hence nothing here */
 
-	DEBUG(3, "%s: Transmit completed\n", dev->name);
+	DEBUG(2, "%s: Transmit completed (FID=%04X)\n", priv->ndev.name, fid);
 
-	stats->tx_packets++;
-	netif_wake_queue(dev);
+	hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
 }
 
-static void __dldwd_ev_alloc(dldwd_priv_t *priv, hermes_t *hw)
+static void __orinoco_ev_alloc(struct orinoco_private *priv, hermes_t *hw)
 {
-	u16 allocfid;
+	struct net_device *dev = &priv->ndev;
+	struct net_device_stats *stats = &priv->stats;
+	u16 fid = hermes_read_regn(hw, ALLOCFID);
 
-	allocfid = hermes_read_regn(hw, ALLOCFID);
-	DEBUG(3, "%s: Allocation complete FID=0x%04x\n", priv->ndev.name, allocfid);
+	DEBUG(3, "%s: Allocation complete FID=0x%04x\n", priv->ndev.name, fid);
 
-	/* For some reason we don't seem to get transmit completed events properly */
-	if (allocfid == priv->txfid)
-		__dldwd_ev_tx(priv, hw);
+	/* We don't generally request Tx complete events to cut down
+	   on the number of interrupts, so we do trasmit complete
+	   processing here, which happens once the firmware is done
+	   with the transmit buffer */
+
+	if (fid != priv->txfid) {
+		printk(KERN_WARNING "%s: Allocate event on unexpected fid (%04X)\n",
+		      dev->name, fid);
+		return;
+	}
+	stats->tx_packets++;
+	netif_wake_queue(dev);
 
-/* 	hermes_write_regn(hw, ALLOCFID, 0); */
+	hermes_write_regn(hw, ALLOCFID, DUMMY_FID);
 }
 
 static void determine_firmware(struct net_device *dev)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	hermes_t *hw = &priv->hw;
 	int err;
 	struct sta_id {
@@ -1311,8 +1471,7 @@
 	u32 firmver;
 
 	/* Get the firmware version */
-	err = HERMES_READ_RECORD(hw, USER_BAP,
-				 HERMES_RID_STAIDENTITY, &sta_id);
+	err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_STAID, &sta_id);
 	if (err) {
 		printk(KERN_WARNING "%s: Error %d reading firmware info. Wildly guessing capabilities...\n",
 		       dev->name, err);
@@ -1338,10 +1497,8 @@
 		       "version %d.%02d\n", dev->name,
 		       sta_id.major, sta_id.minor);
 
-		priv->firmware_type = FIRMWARE_TYPE_LUCENT;
-		priv->tx_rate_ctrl = 0x3;	/* 11 Mb/s auto */
+		priv->firmware_type = FIRMWARE_TYPE_AGERE;
 		priv->need_card_reset = 0;
-		priv->broken_reset = 0;
 		priv->broken_allocate = 0;
 		priv->has_port3 = 1;		/* Still works in 7.28 */
 		priv->has_ibss = (firmver >= 0x60006);
@@ -1353,7 +1510,7 @@
 		priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */
 		priv->has_preamble = 0;
 		priv->ibss_port = 1;
-		/* Tested with Lucent firmware :
+		/* Tested with Agere firmware :
 		 *	1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II
 		 * Tested CableTron firmware : 4.32 => Anton */
 	} else if ((sta_id.vendor == 2) &&
@@ -1365,7 +1522,8 @@
 
 		memset(tmp, 0, sizeof(tmp));
 		/* Get the Symbol firmware version */
-		err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_SYMBOL_SECONDARY_VER,
+		err = hermes_read_ltv(hw, USER_BAP,
+				      HERMES_RID_SECONDARYVERSION_SYMBOL,
 				      SYMBOL_MAX_VER_LEN, NULL, &tmp);
 		if (err) {
 			printk(KERN_WARNING
@@ -1390,9 +1548,7 @@
 		       tmp, firmver);
 
 		priv->firmware_type = FIRMWARE_TYPE_SYMBOL;
-		priv->tx_rate_ctrl = 0xF;	/* 11 Mb/s auto */
 		priv->need_card_reset = 1;
-		priv->broken_reset = 0;
 		priv->broken_allocate = 1;
 		priv->has_port3 = 1;
 		priv->has_ibss = (firmver >= 0x20000);
@@ -1415,9 +1571,7 @@
 		       sta_id.major, sta_id.minor);
 
 		priv->firmware_type = FIRMWARE_TYPE_INTERSIL;
-		priv->tx_rate_ctrl = 0xF;	/* 11 Mb/s auto */
 		priv->need_card_reset = 0;
-		priv->broken_reset = 0;
 		priv->broken_allocate = 0;
 		priv->has_port3 = 1;
 		priv->has_ibss = (firmver >= 0x00007); /* FIXME */
@@ -1431,7 +1585,7 @@
 			priv->ibss_port = 0;
 		else {
 			printk(KERN_NOTICE "%s: Intersil firmware earlier "
-			       "than v0.08 - several features not supported.",
+			       "than v0.08 - several features not supported\n",
 			       dev->name);
 			priv->ibss_port = 1;
 		}
@@ -1443,18 +1597,18 @@
  */
 
 int
-dldwd_init(struct net_device *dev)
+orinoco_init(struct net_device *dev)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	hermes_t *hw = &priv->hw;
 	int err = 0;
-	hermes_id_t nickbuf;
+	struct hermes_idstring nickbuf;
 	u16 reclen;
 	int len;
 
-	TRACE_ENTER("dldwd");
+	TRACE_ENTER("orinoco");
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 
 	/* Do standard firmware reset */
 	err = hermes_reset(hw);
@@ -1467,20 +1621,20 @@
 	determine_firmware(dev);
 
 	if (priv->has_port3)
-		printk(KERN_DEBUG "%s: Ad-hoc demo mode supported.\n", dev->name);
+		printk(KERN_DEBUG "%s: Ad-hoc demo mode supported\n", dev->name);
 	if (priv->has_ibss)
-		printk(KERN_DEBUG "%s: IEEE standard IBSS ad-hoc mode supported.\n",
+		printk(KERN_DEBUG "%s: IEEE standard IBSS ad-hoc mode supported\n",
 		       dev->name);
 	if (priv->has_wep) {
 		printk(KERN_DEBUG "%s: WEP supported, ", dev->name);
 		if (priv->has_big_wep)
-			printk("\"128\"-bit key.\n");
+			printk("104-bit key\n");
 		else
-			printk("40-bit key.\n");
+			printk("40-bit key\n");
 	}
 
 	/* Get the MAC address */
-	err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNF_MACADDR,
+	err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
 			      ETH_ALEN, NULL, dev->dev_addr);
 	if (err) {
 		printk(KERN_WARNING "%s: failed to read MAC address!\n",
@@ -1494,15 +1648,15 @@
 	       dev->dev_addr[5]);
 
 	/* Get the station name */
-	err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNF_NICKNAME,
+	err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
 			      sizeof(nickbuf), &reclen, &nickbuf);
 	if (err) {
-		printk(KERN_ERR "%s: failed to read station name!n",
+		printk(KERN_ERR "%s: failed to read station name\n",
 		       dev->name);
 		goto out;
 	}
 	if (nickbuf.len)
-		len = min_t(u16, IW_ESSID_MAX_SIZE, le16_to_cpu(nickbuf.len));
+		len = min(IW_ESSID_MAX_SIZE, (int)le16_to_cpu(nickbuf.len));
 	else
 		len = min(IW_ESSID_MAX_SIZE, 2 * reclen);
 	memcpy(priv->nick, &nickbuf.val, len);
@@ -1511,7 +1665,8 @@
 	printk(KERN_DEBUG "%s: Station name \"%s\"\n", dev->name, priv->nick);
 
 	/* Get allowed channels */
-	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CHANNEL_LIST, &priv->channel_mask);
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CHANNELLIST,
+				  &priv->channel_mask);
 	if (err) {
 		printk(KERN_ERR "%s: failed to read channel list!\n",
 		       dev->name);
@@ -1519,14 +1674,15 @@
 	}
 
 	/* Get initial AP density */
-	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_SYSTEM_SCALE, &priv->ap_density);
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE, &priv->ap_density);
 	if (err) {
 		printk(KERN_ERR "%s: failed to read AP density!\n", dev->name);
 		goto out;
 	}
 
 	/* Get initial RTS threshold */
-	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_RTS_THRESH, &priv->rts_thresh);
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD,
+				  &priv->rts_thresh);
 	if (err) {
 		printk(KERN_ERR "%s: failed to read RTS threshold!\n", dev->name);
 		goto out;
@@ -1534,10 +1690,11 @@
 
 	/* Get initial fragmentation settings */
 	if (priv->has_mwo)
-		err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_MWO_ROBUST,
+		err = hermes_read_wordrec(hw, USER_BAP,
+					  HERMES_RID_CNFMWOROBUST_AGERE,
 					  &priv->mwo_robust);
 	else
-		err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_FRAG_THRESH,
+		err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
 					  &priv->frag_thresh);
 	if (err) {
 		printk(KERN_ERR "%s: failed to read fragmentation settings!\n", dev->name);
@@ -1548,14 +1705,16 @@
 	if (priv->has_pm) {
 		priv->pm_on = 0;
 		priv->pm_mcast = 1;
-		err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_PM_PERIOD,
+		err = hermes_read_wordrec(hw, USER_BAP,
+					  HERMES_RID_CNFMAXSLEEPDURATION,
 					  &priv->pm_period);
 		if (err) {
 			printk(KERN_ERR "%s: failed to read power management period!\n",
 			       dev->name);
 			goto out;
 		}
-		err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_PM_HOLDOVER,
+		err = hermes_read_wordrec(hw, USER_BAP,
+					  HERMES_RID_CNFPMHOLDOVERDURATION,
 					  &priv->pm_timeout);
 		if (err) {
 			printk(KERN_ERR "%s: failed to read power management timeout!\n",
@@ -1566,7 +1725,8 @@
 
 	/* Preamble setup */
 	if (priv->has_preamble) {
-		err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_SYMBOL_PREAMBLE, &priv->preamble);
+		err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFPREAMBLE_SYMBOL,
+					  &priv->preamble);
 		if (err)
 			goto out;
 	}
@@ -1578,40 +1738,39 @@
 	set_port_type(priv);
 
 	priv->promiscuous = 0;
-	priv->allmulti = 0;
 	priv->wep_on = 0;
 	priv->tx_key = 0;
 
 	printk(KERN_DEBUG "%s: ready\n", dev->name);
 
  out:
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
-	TRACE_EXIT("dldwd");
+	TRACE_EXIT("orinoco");
 
 	return err;
 }
 
 struct net_device_stats *
-dldwd_get_stats(struct net_device *dev)
+orinoco_get_stats(struct net_device *dev)
 {
-	dldwd_priv_t *priv = (dldwd_priv_t *)dev->priv;
+	struct orinoco_private *priv = (struct orinoco_private *)dev->priv;
 	
 	return &priv->stats;
 }
 
 struct iw_statistics *
-dldwd_get_wireless_stats(struct net_device *dev)
+orinoco_get_wireless_stats(struct net_device *dev)
 {
-	dldwd_priv_t *priv = (dldwd_priv_t *)dev->priv;
+	struct orinoco_private *priv = (struct orinoco_private *)dev->priv;
 	hermes_t *hw = &priv->hw;
 	struct iw_statistics *wstats = &priv->wstats;
 	int err = 0;
 
-	if (!priv->hw_ready)
-		return NULL;
+	if (! netif_device_present(dev))
+		return NULL; /* FIXME: We may be able to do better than this */
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 
 	if (priv->iw_mode == IW_MODE_ADHOC) {
 		memset(&wstats->qual, 0, sizeof(wstats->qual));
@@ -1626,7 +1785,7 @@
 		}
 #endif /* WIRELESS_SPY */
 	} else {
-		dldwd_commsqual_t cq;
+		orinoco_commsqual_t cq;
 
 		err = HERMES_READ_RECORD(hw, USER_BAP,
 					 HERMES_RID_COMMSQUALITY, &cq);
@@ -1640,7 +1799,14 @@
 		wstats->qual.updated = 7;
 	}
 
-	dldwd_unlock(priv);
+	/* We can't really wait for the tallies inquiry command to
+	 * complete, so we just use the previous results and trigger
+	 * a new tallies inquiry command for next time - Jean II */
+	/* Hmm.. seems a bit ugly, I wonder if there's a way to do
+	   better - dgibson */
+	err = hermes_inquire(hw, HERMES_INQ_TALLIES);
+               
+	orinoco_unlock(priv);
 
 	if (err)
 		return NULL;
@@ -1649,10 +1815,10 @@
 }
 
 #ifdef WIRELESS_SPY
-static inline void dldwd_spy_gather(struct net_device *dev, u_char *mac,
+static inline void orinoco_spy_gather(struct net_device *dev, u_char *mac,
 				    int level, int noise)
 {
-	dldwd_priv_t *priv = (dldwd_priv_t *)dev->priv;
+	struct orinoco_private *priv = (struct orinoco_private *)dev->priv;
 	int i;
 
 	/* Gather wireless spy statistics: for each packet, compare the
@@ -1668,11 +1834,11 @@
 #endif /* WIRELESS_SPY */
 
 void
-dldwd_stat_gather( struct net_device *dev,
+orinoco_stat_gather( struct net_device *dev,
 		   struct sk_buff *skb,
-		   struct dldwd_frame_hdr *hdr)
+		   struct orinoco_rxframe_hdr *hdr)
 {
-	dldwd_priv_t *priv = (dldwd_priv_t *)dev->priv;
+	struct orinoco_private *priv = (struct orinoco_private *)dev->priv;
 
 	/* Using spy support with lots of Rx packets, like in an
 	 * infrastructure (AP), will really slow down everything, because
@@ -1686,20 +1852,16 @@
 	/* Note : gcc will optimise the whole section away if
 	 * WIRELESS_SPY is not defined... - Jean II */
 	if (priv->spy_number > 0) {
-		u8 *stats = (u8 *) &(hdr->desc.q_info);
-		/* This code may look strange. Everywhere we are using 16 bit
-		 * ints except here. I've verified that these are are the
-		 * correct values. Please check on PPC - Jean II */
-
-		dldwd_spy_gather(dev, skb->mac.raw + ETH_ALEN, (int)stats[1], (int)stats[0]);
+		orinoco_spy_gather(dev, skb->mac.raw + ETH_ALEN,
+				   hdr->desc.signal, hdr->desc.silence);
 	}
 #endif /* WIRELESS_SPY */
 }
 
 int
-dldwd_xmit(struct sk_buff *skb, struct net_device *dev)
+orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
 {
-	dldwd_priv_t *priv = (dldwd_priv_t *)dev->priv;
+	struct orinoco_private *priv = (struct orinoco_private *)dev->priv;
 	struct net_device_stats *stats = &priv->stats;
 	hermes_t *hw = &priv->hw;
 	int err = 0;
@@ -1707,7 +1869,7 @@
 	char *p;
 	struct ethhdr *eh;
 	int len, data_len, data_off;
-	struct dldwd_frame_hdr hdr;
+	struct orinoco_txframe_hdr hdr;
 	hermes_response_t resp;
 
 	if (! netif_running(dev)) {
@@ -1723,7 +1885,7 @@
 		return 1;
 	}
 	
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 
 	/* Length of the packet body */
 	len = max_t(int,skb->len - ETH_HLEN, ETH_ZLEN);
@@ -1734,7 +1896,11 @@
 	memset(&hdr, 0, sizeof(hdr));
 	memcpy(hdr.p80211.addr1, eh->h_dest, ETH_ALEN);
 	memcpy(hdr.p80211.addr2, eh->h_source, ETH_ALEN);
-	hdr.p80211.frame_ctl = DLDWD_FTYPE_DATA;
+	hdr.p80211.frame_ctl = IEEE802_11_FTYPE_DATA;
+
+	/* Request an interrupt on Tx failures, but not sucesses (we
+           use the buffer reclaim allocation event instead */
+	hdr.desc.tx_control = HERMES_TXCTRL_TX_EX;
 
 	/* Encapsulate Ethernet-II frames */
 	if (ntohs(eh->h_proto) > 1500) { /* Ethernet-II frame */
@@ -1751,17 +1917,13 @@
 		hdr.p8023.h_proto = htons(data_len + ENCAPS_OVERHEAD);
 		
 		/* 802.2 header */
-		if (! use_old_encaps) 
-			memcpy(&hdr.p8022, &encaps_hdr,
-			       sizeof(encaps_hdr));
-		else
-			memcpy(&hdr.p8022, &encaps_hdr,
-			       sizeof(old_encaps_hdr));
+		memcpy(&hdr.p8022, &encaps_hdr, sizeof(encaps_hdr));
 			
 		hdr.ethertype = eh->h_proto;
 		err  = hermes_bap_pwrite(hw, USER_BAP, &hdr, sizeof(hdr),
 					 txfid, 0);
 		if (err) {
+#if 0
 			if (err == -EIO)
 				/* We get these errors reported by the
 				   firmware every so often apparently at
@@ -1769,6 +1931,7 @@
 				   handle the retry */
 				DEBUG(1, "%s: DEBUG: EIO writing packet header to BAP\n", dev->name);
 			else
+#endif
 				printk(KERN_ERR "%s: Error %d writing packet header to BAP\n",
 				       dev->name, err);
 			stats->tx_errors++;
@@ -1795,10 +1958,10 @@
 	/* Round up for odd length packets */
 	err = hermes_bap_pwrite(hw, USER_BAP, p, RUP_EVEN(data_len), txfid, data_off);
 	if (err) {
-		if (err == -EIO)
-			DEBUG(1, "%s: DEBUG: EIO writing packet header to BAP\n", dev->name);
-		else
-			printk(KERN_ERR "%s: Error %d writing packet header to BAP",
+/*  		if (err == -EIO) */
+/*  			DEBUG(1, "%s: DEBUG: EIO writing packet header to BAP\n", dev->name); */
+/*  		else */
+			printk(KERN_ERR "%s: Error %d writing packet header to BAP\n",
 			       dev->name, err);
 		stats->tx_errors++;
 		goto fail;
@@ -1817,21 +1980,21 @@
 
 	netif_stop_queue(dev);
 
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	dev_kfree_skb(skb);
 
 	return 0;
  fail:
 
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 	return err;
 }
 
 void
-dldwd_tx_timeout(struct net_device *dev)
+orinoco_tx_timeout(struct net_device *dev)
 {
-	dldwd_priv_t *priv = (dldwd_priv_t *)dev->priv;
+	struct orinoco_private *priv = (struct orinoco_private *)dev->priv;
 	struct net_device_stats *stats = &priv->stats;
 	int err = 0;
 
@@ -1839,7 +2002,7 @@
 
 	stats->tx_errors++;
 
-	err = dldwd_reset(priv);
+	err = orinoco_reset(priv);
 	if (err)
 		printk(KERN_ERR "%s: Error %d resetting card on Tx timeout!\n",
 		       dev->name, err);
@@ -1849,9 +2012,9 @@
 	}
 }
 
-static int dldwd_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq)
+static int orinoco_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	int err = 0;
 	int mode;
 	struct iw_range range;
@@ -1866,9 +2029,9 @@
 
 	rrq->length = sizeof(range);
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 	mode = priv->iw_mode;
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	memset(&range, 0, sizeof(range));
 
@@ -1904,13 +2067,25 @@
 		range.max_qual.qual = 0;
 		range.max_qual.level = 0;
 		range.max_qual.noise = 0;
+#if WIRELESS_EXT > 11
+		range.avg_qual.qual = 0;
+		range.avg_qual.level = 0;
+		range.avg_qual.noise = 0;
+#endif /* WIRELESS_EXT > 11 */
+
 	} else {
 		range.max_qual.qual = 0x8b - 0x2f;
 		range.max_qual.level = 0x2f - 0x95 - 1;
 		range.max_qual.noise = 0x2f - 0x95 - 1;
+#if WIRELESS_EXT > 11
+		/* Need to get better values */
+		range.avg_qual.qual = 0x24;
+		range.avg_qual.level = 0xC2;
+		range.avg_qual.noise = 0x9E;
+#endif /* WIRELESS_EXT > 11 */
 	}
 
-	err = dldwd_hw_get_bitratelist(priv, &numrates,
+	err = orinoco_hw_get_bitratelist(priv, &numrates,
 				       range.bitrate, IW_MAX_BITRATES);
 	if (err)
 		return err;
@@ -1929,9 +2104,9 @@
 	range.min_frag = 256;
 	range.max_frag = 2346;
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 	if (priv->has_wep) {
-		range.max_encoding_tokens = MAX_KEYS;
+		range.max_encoding_tokens = ORINOCO_MAX_KEYS;
 
 		range.encoding_size[0] = SMALL_KEY_SIZE;
 		range.num_encoding_sizes = 1;
@@ -1944,7 +2119,7 @@
 		range.num_encoding_sizes = 0;
 		range.max_encoding_tokens = 0;
 	}
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 		
 	range.min_pmp = 0;
 	range.max_pmp = 65535000;
@@ -1976,30 +2151,30 @@
 	return 0;
 }
 
-static int dldwd_ioctl_setiwencode(struct net_device *dev, struct iw_point *erq)
+static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *erq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	int index = (erq->flags & IW_ENCODE_INDEX) - 1;
 	int setindex = priv->tx_key;
 	int enable = priv->wep_on;
 	int restricted = priv->wep_restrict;
 	u16 xlen = 0;
 	int err = 0;
-	char keybuf[MAX_KEY_SIZE];
+	char keybuf[ORINOCO_MAX_KEY_SIZE];
 	
 	if (erq->pointer) {
 		/* We actually have a key to set */
-		if ( (erq->length < SMALL_KEY_SIZE) || (erq->length > MAX_KEY_SIZE) )
+		if ( (erq->length < SMALL_KEY_SIZE) || (erq->length > ORINOCO_MAX_KEY_SIZE) )
 			return -EINVAL;
 		
 		if (copy_from_user(keybuf, erq->pointer, erq->length))
 			return -EFAULT;
 	}
 	
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 	
 	if (erq->pointer) {
-		if (erq->length > MAX_KEY_SIZE) {
+		if (erq->length > ORINOCO_MAX_KEY_SIZE) {
 			err = -E2BIG;
 			goto out;
 		}
@@ -2010,7 +2185,7 @@
 			goto out;
 		}
 		
-		if ((index < 0) || (index >= MAX_KEYS))
+		if ((index < 0) || (index >= ORINOCO_MAX_KEYS))
 			index = priv->tx_key;
 		
 		if (erq->length > SMALL_KEY_SIZE) {
@@ -2029,7 +2204,7 @@
 		/* Important note : if the user do "iwconfig eth0 enc off",
 		 * we will arrive there with an index of -1. This is valid
 		 * but need to be taken care off... Jean II */
-		if ((index < 0) || (index >= MAX_KEYS)) {
+		if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) {
 			if((index != -1) || (erq->flags == 0)) {
 				err = -EINVAL;
 				goto out;
@@ -2062,22 +2237,22 @@
 	priv->wep_restrict = restricted;
 	
  out:
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
-	return 0;
+	return err;
 }
 
-static int dldwd_ioctl_getiwencode(struct net_device *dev, struct iw_point *erq)
+static int orinoco_ioctl_getiwencode(struct net_device *dev, struct iw_point *erq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	int index = (erq->flags & IW_ENCODE_INDEX) - 1;
 	u16 xlen = 0;
-	char keybuf[MAX_KEY_SIZE];
+	char keybuf[ORINOCO_MAX_KEY_SIZE];
 
 	
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 
-	if ((index < 0) || (index >= MAX_KEYS))
+	if ((index < 0) || (index >= ORINOCO_MAX_KEYS))
 		index = priv->tx_key;
 
 	erq->flags = 0;
@@ -2086,7 +2261,7 @@
 	erq->flags |= index + 1;
 	
 	/* Only for symbol cards - Jean II */
-	if (priv->firmware_type != FIRMWARE_TYPE_LUCENT) {
+	if (priv->firmware_type != FIRMWARE_TYPE_AGERE) {
 		if(priv->wep_restrict)
 			erq->flags |= IW_ENCODE_RESTRICTED;
 		else
@@ -2098,10 +2273,10 @@
 	erq->length = xlen;
 
 	if (erq->pointer) {
-		memcpy(keybuf, priv->keys[index].data, MAX_KEY_SIZE);
+		memcpy(keybuf, priv->keys[index].data, ORINOCO_MAX_KEY_SIZE);
 	}
 	
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	if (erq->pointer) {
 		if (copy_to_user(erq->pointer, keybuf, xlen))
@@ -2111,9 +2286,9 @@
 	return 0;
 }
 
-static int dldwd_ioctl_setessid(struct net_device *dev, struct iw_point *erq)
+static int orinoco_ioctl_setessid(struct net_device *dev, struct iw_point *erq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	char essidbuf[IW_ESSID_MAX_SIZE+1];
 
 	/* Note : ESSID is ignored in Ad-Hoc demo mode, but we can set it
@@ -2131,25 +2306,25 @@
 		essidbuf[erq->length] = '\0';
 	}
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 
 	memcpy(priv->desired_essid, essidbuf, sizeof(priv->desired_essid));
 
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	return 0;
 }
 
-static int dldwd_ioctl_getessid(struct net_device *dev, struct iw_point *erq)
+static int orinoco_ioctl_getessid(struct net_device *dev, struct iw_point *erq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	char essidbuf[IW_ESSID_MAX_SIZE+1];
 	int active;
 	int err = 0;
 
 	TRACE_ENTER(dev->name);
 
-	err = dldwd_hw_get_essid(priv, &active, essidbuf);
+	err = orinoco_hw_get_essid(priv, &active, essidbuf);
 	if (err)
 		return err;
 
@@ -2164,9 +2339,9 @@
 	return 0;
 }
 
-static int dldwd_ioctl_setnick(struct net_device *dev, struct iw_point *nrq)
+static int orinoco_ioctl_setnick(struct net_device *dev, struct iw_point *nrq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	char nickbuf[IW_ESSID_MAX_SIZE+1];
 
 	if (nrq->length > IW_ESSID_MAX_SIZE)
@@ -2179,23 +2354,23 @@
 
 	nickbuf[nrq->length] = '\0';
 	
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 
 	memcpy(priv->nick, nickbuf, sizeof(priv->nick));
 
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	return 0;
 }
 
-static int dldwd_ioctl_getnick(struct net_device *dev, struct iw_point *nrq)
+static int orinoco_ioctl_getnick(struct net_device *dev, struct iw_point *nrq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	char nickbuf[IW_ESSID_MAX_SIZE+1];
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 	memcpy(nickbuf, priv->nick, IW_ESSID_MAX_SIZE+1);
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	nrq->length = strlen(nickbuf)+1;
 
@@ -2205,9 +2380,9 @@
 	return 0;
 }
 
-static int dldwd_ioctl_setfreq(struct net_device *dev, struct iw_freq *frq)
+static int orinoco_ioctl_setfreq(struct net_device *dev, struct iw_freq *frq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	int chan = -1;
 
 	/* We can only use this in Ad-Hoc demo mode to set the operating
@@ -2236,23 +2411,23 @@
 	     ! (priv->channel_mask & (1 << (chan-1)) ) )
 		return -EINVAL;
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 	priv->channel = chan;
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	return 0;
 }
 
-static int dldwd_ioctl_getsens(struct net_device *dev, struct iw_param *srq)
+static int orinoco_ioctl_getsens(struct net_device *dev, struct iw_param *srq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	hermes_t *hw = &priv->hw;
 	u16 val;
 	int err;
 
-	dldwd_lock(priv);
-	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_SYSTEM_SCALE, &val);
-	dldwd_unlock(priv);
+	orinoco_lock(priv);
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE, &val);
+	orinoco_unlock(priv);
 
 	if (err)
 		return err;
@@ -2263,24 +2438,24 @@
 	return 0;
 }
 
-static int dldwd_ioctl_setsens(struct net_device *dev, struct iw_param *srq)
+static int orinoco_ioctl_setsens(struct net_device *dev, struct iw_param *srq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	int val = srq->value;
 
 	if ((val < 1) || (val > 3))
 		return -EINVAL;
 	
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 	priv->ap_density = val;
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	return 0;
 }
 
-static int dldwd_ioctl_setrts(struct net_device *dev, struct iw_param *rrq)
+static int orinoco_ioctl_setrts(struct net_device *dev, struct iw_param *rrq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	int val = rrq->value;
 
 	if (rrq->disabled)
@@ -2289,19 +2464,19 @@
 	if ( (val < 0) || (val > 2347) )
 		return -EINVAL;
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 	priv->rts_thresh = val;
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	return 0;
 }
 
-static int dldwd_ioctl_setfrag(struct net_device *dev, struct iw_param *frq)
+static int orinoco_ioctl_setfrag(struct net_device *dev, struct iw_param *frq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	int err = 0;
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 
 	if (priv->has_mwo) {
 		if (frq->disabled)
@@ -2323,22 +2498,24 @@
 		}
 	}
 
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	return err;
 }
 
-static int dldwd_ioctl_getfrag(struct net_device *dev, struct iw_param *frq)
+static int orinoco_ioctl_getfrag(struct net_device *dev, struct iw_param *frq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	hermes_t *hw = &priv->hw;
 	int err = 0;
 	u16 val;
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 	
 	if (priv->has_mwo) {
-		err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_MWO_ROBUST, &val);
+		err = hermes_read_wordrec(hw, USER_BAP,
+					  HERMES_RID_CNFMWOROBUST_AGERE,
+					  &val);
 		if (err)
 			val = 0;
 
@@ -2346,7 +2523,8 @@
 		frq->disabled = ! val;
 		frq->fixed = 0;
 	} else {
-		err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_FRAG_THRESH, &val);
+		err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
+					  &val);
 		if (err)
 			val = 0;
 
@@ -2355,158 +2533,122 @@
 		frq->fixed = 1;
 	}
 
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 	
 	return err;
 }
 
-static int dldwd_ioctl_setrate(struct net_device *dev, struct iw_param *rrq)
+static int orinoco_ioctl_setrate(struct net_device *dev, struct iw_param *rrq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	int err = 0;
-	int rate_ctrl = -1;
-	int fixed, upto;
-	int brate;
+	int ratemode = -1;
+	int bitrate; /* 100s of kilobits */
 	int i;
+	
+	/* As the user space doesn't know our highest rate, it uses -1
+	 * to ask us to set the highest rate.  Test it using "iwconfig
+	 * ethX rate auto" - Jean II */
+	if (rrq->value == -1)
+		bitrate = 110;
+	else {
+		if (rrq->value % 100000)
+			return -EINVAL;
+		bitrate = rrq->value / 100000;
+	}
 
-	dldwd_lock(priv);
-
-	/* Normalise value */
-	brate = rrq->value / 500000;
+	if ( (bitrate != 10) && (bitrate != 20) &&
+	     (bitrate != 55) && (bitrate != 110) )
+		return -EINVAL;
 
-	switch (priv->firmware_type) {
-	case FIRMWARE_TYPE_LUCENT: /* Lucent style rate */
-		if (! rrq->fixed) {
-			if (brate > 0)
-				brate = -brate;
-			else
-				brate = -22;
-		}
-	
-		for (i = 0; i < NUM_RATES; i++)
-			if (rate_list[i] == brate) {
-				rate_ctrl = i;
-				break;
-			}
-	
-		if ( (rate_ctrl < 1) || (rate_ctrl >= NUM_RATES) )
-			err = -EINVAL;
-		else
-			priv->tx_rate_ctrl = rate_ctrl;
-		break;
-	case FIRMWARE_TYPE_INTERSIL: /* Intersil style rate */
-	case FIRMWARE_TYPE_SYMBOL: /* Symbol style rate */
-		switch(brate) {
-		case 0:
-			fixed = 0x0;
-			upto = 0xF;
-			break;
-		case 2:
-			fixed = 0x1;
-			upto = 0x1;
-			break;
-		case 4:
-			fixed = 0x2;
-			upto = 0x3;
-			break;
-		case 11:
-			fixed = 0x4;
-			upto = 0x7;
-			break;
-		case 22:
-			fixed = 0x8;
-			upto = 0xF;
+	for (i = 0; i < BITRATE_TABLE_SIZE; i++)
+		if ( (bitrate_table[i].bitrate == bitrate) &&
+		     (bitrate_table[i].automatic == ! rrq->fixed) ) {
+			ratemode = i;
 			break;
-		default:
-			fixed = 0x0;
-			upto = 0x0;
 		}
-		if (rrq->fixed)
-			rate_ctrl = fixed;
-		else
-			rate_ctrl = upto;
-		if (rate_ctrl == 0)
-			err = -EINVAL;
-		else
-			priv->tx_rate_ctrl = rate_ctrl;
-		break;
-	}
+	
+	if (ratemode == -1)
+		return -EINVAL;
 
-	dldwd_unlock(priv);
+	orinoco_lock(priv);
+	priv->bitratemode = ratemode;
+	orinoco_unlock(priv);
 
 	return err;
 }
 
-static int dldwd_ioctl_getrate(struct net_device *dev, struct iw_param *rrq)
+static int orinoco_ioctl_getrate(struct net_device *dev, struct iw_param *rrq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	hermes_t *hw = &priv->hw;
 	int err = 0;
+	int ratemode;
+	int i;
 	u16 val;
-	int brate = 0;
 
-	dldwd_lock(priv);
-	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_TX_RATE_CTRL, &val);
-	if (err)
-		goto out;
-	
-	switch (priv->firmware_type) {
-	case FIRMWARE_TYPE_LUCENT: /* Lucent style rate */
-		brate = rate_list[val];
-	
-		if (brate < 0) {
-			rrq->fixed = 0;
+	orinoco_lock(priv);
 
-			err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENT_TX_RATE, &val);
-			if (err)
-				goto out;
+	ratemode = priv->bitratemode;
+
+	if ( (ratemode < 0) || (ratemode > BITRATE_TABLE_SIZE) )
+		BUG();
+
+	rrq->value = bitrate_table[ratemode].bitrate * 100000;
+	rrq->fixed = ! bitrate_table[ratemode].automatic;
+	rrq->disabled = 0;
 
+	/* If the interface is running we try to find more about the
+	   current mode */
+	if (netif_running(dev)) {
+		err = hermes_read_wordrec(hw, USER_BAP,
+					  HERMES_RID_CURRENTTXRATE, &val);
+		if (err)
+			goto out;
+		
+		switch (priv->firmware_type) {
+		case FIRMWARE_TYPE_AGERE: /* Lucent style rate */
+			/* Note : in Lucent firmware, the return value of
+			 * HERMES_RID_CURRENTTXRATE is the bitrate in Mb/s,
+			 * and therefore is totally different from the
+			 * encoding of HERMES_RID_CNFTXRATECONTROL.
+			 * Don't forget that 6Mb/s is really 5.5Mb/s */
 			if (val == 6)
-				brate = 11;
+				rrq->value = 5500000;
 			else
-				brate = 2*val;
-		} else
-			rrq->fixed = 1;
-		break;
-	case FIRMWARE_TYPE_INTERSIL: /* Intersil style rate */
-	case FIRMWARE_TYPE_SYMBOL: /* Symbol style rate */
-		/* Check if auto or fixed (crude approximation) */
-		if((val & 0x1) && (val > 1)) {
-			rrq->fixed = 0;
-
-			err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENT_TX_RATE, &val);
-			if (err)
-				goto out;
-		} else
-			rrq->fixed = 1;
+				rrq->value = val * 1000000;
+                        break;
+		case FIRMWARE_TYPE_INTERSIL: /* Intersil style rate */
+		case FIRMWARE_TYPE_SYMBOL: /* Symbol style rate */
+			for (i = 0; i < BITRATE_TABLE_SIZE; i++)
+				if (bitrate_table[i].intersil_txratectrl == val) {
+					ratemode = i;
+					break;
+				}
+			if (i >= BITRATE_TABLE_SIZE)
+				printk(KERN_INFO "%s: Unable to determine current bitrate (0x%04hx)\n",
+				       dev->name, val);
 
-		if(val >= 8)
-			brate = 22;
-		else if(val >= 4)
-			brate = 11;
-		else if(val >= 2)
-			brate = 4;
-		else
-			brate = 2;
-		break;
+			rrq->value = bitrate_table[ratemode].bitrate * 100000;
+			break;
+		default:
+			BUG();
+		}
 	}
 
-	rrq->value = brate * 500000;
-	rrq->disabled = 0;
-
  out:
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	return err;
 }
 
-static int dldwd_ioctl_setpower(struct net_device *dev, struct iw_param *prq)
+static int orinoco_ioctl_setpower(struct net_device *dev, struct iw_param *prq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	int err = 0;
 
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 
 	if (prq->disabled) {
 		priv->pm_on = 0;
@@ -2546,33 +2688,34 @@
 	}
 
  out:
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	return err;
 }
 
-static int dldwd_ioctl_getpower(struct net_device *dev, struct iw_param *prq)
+static int orinoco_ioctl_getpower(struct net_device *dev, struct iw_param *prq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	hermes_t *hw = &priv->hw;
 	int err = 0;
 	u16 enable, period, timeout, mcast;
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 	
-	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_PM_ENABLE, &enable);
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFPMENABLED, &enable);
 	if (err)
 		goto out;
 
-	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_PM_PERIOD, &period);
+	err = hermes_read_wordrec(hw, USER_BAP,
+				  HERMES_RID_CNFMAXSLEEPDURATION, &period);
 	if (err)
 		goto out;
 
-	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_PM_HOLDOVER, &timeout);
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFPMHOLDOVERDURATION, &timeout);
 	if (err)
 		goto out;
 
-	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_PM_MCAST_RX, &mcast);
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFMULTICASTRECEIVE, &mcast);
 	if (err)
 		goto out;
 
@@ -2591,30 +2734,33 @@
 		prq->flags |= IW_POWER_UNICAST_R;
 
  out:
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	return err;
 }
 
 #if WIRELESS_EXT > 10
-static int dldwd_ioctl_getretry(struct net_device *dev, struct iw_param *rrq)
+static int orinoco_ioctl_getretry(struct net_device *dev, struct iw_param *rrq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	hermes_t *hw = &priv->hw;
 	int err = 0;
 	u16 short_limit, long_limit, lifetime;
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 	
-	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_SHORT_RETRY_LIMIT, &short_limit);
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_SHORTRETRYLIMIT,
+				  &short_limit);
 	if (err)
 		goto out;
 
-	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_LONG_RETRY_LIMIT, &long_limit);
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_LONGRETRYLIMIT,
+				  &long_limit);
 	if (err)
 		goto out;
 
-	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_MAX_TX_LIFETIME, &lifetime);
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_MAXTRANSMITLIFETIME,
+				  &lifetime);
 	if (err)
 		goto out;
 
@@ -2638,46 +2784,46 @@
 	}
 
  out:
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	return err;
 }
 #endif /* WIRELESS_EXT > 10 */
 
-static int dldwd_ioctl_setibssport(struct net_device *dev, struct iwreq *wrq)
+static int orinoco_ioctl_setibssport(struct net_device *dev, struct iwreq *wrq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	int val = *( (int *) wrq->u.name );
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 	priv->ibss_port = val ;
 
 	/* Actually update the mode we are using */
 	set_port_type(priv);
 
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 	return 0;
 }
 
-static int dldwd_ioctl_getibssport(struct net_device *dev, struct iwreq *wrq)
+static int orinoco_ioctl_getibssport(struct net_device *dev, struct iwreq *wrq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	int *val = (int *)wrq->u.name;
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 	*val = priv->ibss_port;
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	return 0;
 }
 
-static int dldwd_ioctl_setport3(struct net_device *dev, struct iwreq *wrq)
+static int orinoco_ioctl_setport3(struct net_device *dev, struct iwreq *wrq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	int val = *( (int *) wrq->u.name );
 	int err = 0;
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 	switch (val) {
 	case 0: /* Try to do IEEE ad-hoc mode */
 		if (! priv->has_ibss) {
@@ -2706,28 +2852,28 @@
 		/* Actually update the mode we are using */
 		set_port_type(priv);
 
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	return err;
 }
 
-static int dldwd_ioctl_getport3(struct net_device *dev, struct iwreq *wrq)
+static int orinoco_ioctl_getport3(struct net_device *dev, struct iwreq *wrq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	int *val = (int *)wrq->u.name;
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 	*val = priv->prefer_port3;
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	return 0;
 }
 
 /* Spy is used for link quality/strength measurements in Ad-Hoc mode
  * Jean II */
-static int dldwd_ioctl_setspy(struct net_device *dev, struct iw_point *srq)
+static int orinoco_ioctl_setspy(struct net_device *dev, struct iw_point *srq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	struct sockaddr address[IW_MAX_SPY];
 	int number = srq->length;
 	int i;
@@ -2745,9 +2891,9 @@
 	}
 
 	/* Make sure nobody mess with the structure while we do */
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 
-	/* dldwd_lock() doesn't disable interrupts, so make sure the
+	/* orinoco_lock() doesn't disable interrupts, so make sure the
 	 * interrupt rx path don't get confused while we copy */
 	priv->spy_number = 0;
 
@@ -2774,20 +2920,20 @@
 	}
 
 	/* Now, let the others play */
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	return err;
 }
 
-static int dldwd_ioctl_getspy(struct net_device *dev, struct iw_point *srq)
+static int orinoco_ioctl_getspy(struct net_device *dev, struct iw_point *srq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	struct sockaddr address[IW_MAX_SPY];
 	struct iw_quality spy_stat[IW_MAX_SPY];
 	int number;
 	int i;
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 
 	number = priv->spy_number;
 	if ((number > 0) && (srq->pointer)) {
@@ -2807,7 +2953,7 @@
 			priv->spy_stat[i].updated = 0;
 	}
 
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	/* Push stuff to user space */
 	srq->length = number;
@@ -2822,22 +2968,22 @@
 }
 
 int
-dldwd_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	struct iwreq *wrq = (struct iwreq *)rq;
 	int err = 0;
 	int changed = 0;
 
 	TRACE_ENTER(dev->name);
 
-	/* In theory, we could allow most of the the SET stuff to be done
-	 * In practice, the laps of time at startup when the card is not
-	 * ready is very short, so why bother...
-	 * Note that hw_ready is different from up/down (ifconfig), when
-	 * the device is not yet up, it is usually already ready...
-	 * Jean II */
-	if (!priv->hw_ready)
+	/* In theory, we could allow most of the the SET stuff to be
+	 * done In practice, the laps of time at startup when the card
+	 * is not ready is very short, so why bother...  Note that
+	 * netif_device_present is different from up/down (ifconfig),
+	 * when the device is not yet up, it is usually already
+	 * ready...  Jean II */
+	if (! netif_device_present(dev))
 		return -ENODEV;
 
 	switch (cmd) {
@@ -2849,17 +2995,17 @@
 	case SIOCGIWAP:
 		DEBUG(1, "%s: SIOCGIWAP\n", dev->name);
 		wrq->u.ap_addr.sa_family = ARPHRD_ETHER;
-		err = dldwd_hw_get_bssid(priv, wrq->u.ap_addr.sa_data);
+		err = orinoco_hw_get_bssid(priv, wrq->u.ap_addr.sa_data);
 		break;
 
 	case SIOCGIWRANGE:
 		DEBUG(1, "%s: SIOCGIWRANGE\n", dev->name);
-		err = dldwd_ioctl_getiwrange(dev, &wrq->u.data);
+		err = orinoco_ioctl_getiwrange(dev, &wrq->u.data);
 		break;
 
 	case SIOCSIWMODE:
 		DEBUG(1, "%s: SIOCSIWMODE\n", dev->name);
-		dldwd_lock(priv);
+		orinoco_lock(priv);
 		switch (wrq->u.mode) {
 		case IW_MODE_ADHOC:
 			if (! (priv->has_ibss || priv->has_port3) )
@@ -2880,14 +3026,14 @@
 			break;
 		}
 		set_port_type(priv);
-		dldwd_unlock(priv);
+		orinoco_unlock(priv);
 		break;
 
 	case SIOCGIWMODE:
 		DEBUG(1, "%s: SIOCGIWMODE\n", dev->name);
-		dldwd_lock(priv);
+		orinoco_lock(priv);
 		wrq->u.mode = priv->iw_mode;
-		dldwd_unlock(priv);
+		orinoco_unlock(priv);
 		break;
 
 	case SIOCSIWENCODE:
@@ -2897,7 +3043,7 @@
 			break;
 		}
 
-		err = dldwd_ioctl_setiwencode(dev, &wrq->u.encoding);
+		err = orinoco_ioctl_setiwencode(dev, &wrq->u.encoding);
 		if (! err)
 			changed = 1;
 		break;
@@ -2914,54 +3060,54 @@
 			break;
 		}
 
-		err = dldwd_ioctl_getiwencode(dev, &wrq->u.encoding);
+		err = orinoco_ioctl_getiwencode(dev, &wrq->u.encoding);
 		break;
 
 	case SIOCSIWESSID:
 		DEBUG(1, "%s: SIOCSIWESSID\n", dev->name);
-		err = dldwd_ioctl_setessid(dev, &wrq->u.essid);
+		err = orinoco_ioctl_setessid(dev, &wrq->u.essid);
 		if (! err)
 			changed = 1;
 		break;
 
 	case SIOCGIWESSID:
 		DEBUG(1, "%s: SIOCGIWESSID\n", dev->name);
-		err = dldwd_ioctl_getessid(dev, &wrq->u.essid);
+		err = orinoco_ioctl_getessid(dev, &wrq->u.essid);
 		break;
 
 	case SIOCSIWNICKN:
 		DEBUG(1, "%s: SIOCSIWNICKN\n", dev->name);
-		err = dldwd_ioctl_setnick(dev, &wrq->u.data);
+		err = orinoco_ioctl_setnick(dev, &wrq->u.data);
 		if (! err)
 			changed = 1;
 		break;
 
 	case SIOCGIWNICKN:
 		DEBUG(1, "%s: SIOCGIWNICKN\n", dev->name);
-		err = dldwd_ioctl_getnick(dev, &wrq->u.data);
+		err = orinoco_ioctl_getnick(dev, &wrq->u.data);
 		break;
 
 	case SIOCGIWFREQ:
 		DEBUG(1, "%s: SIOCGIWFREQ\n", dev->name);
-		wrq->u.freq.m = dldwd_hw_get_freq(priv);
+		wrq->u.freq.m = orinoco_hw_get_freq(priv);
 		wrq->u.freq.e = 1;
 		break;
 
 	case SIOCSIWFREQ:
 		DEBUG(1, "%s: SIOCSIWFREQ\n", dev->name);
-		err = dldwd_ioctl_setfreq(dev, &wrq->u.freq);
+		err = orinoco_ioctl_setfreq(dev, &wrq->u.freq);
 		if (! err)
 			changed = 1;
 		break;
 
 	case SIOCGIWSENS:
 		DEBUG(1, "%s: SIOCGIWSENS\n", dev->name);
-		err = dldwd_ioctl_getsens(dev, &wrq->u.sens);
+		err = orinoco_ioctl_getsens(dev, &wrq->u.sens);
 		break;
 
 	case SIOCSIWSENS:
 		DEBUG(1, "%s: SIOCSIWSENS\n", dev->name);
-		err = dldwd_ioctl_setsens(dev, &wrq->u.sens);
+		err = orinoco_ioctl_setsens(dev, &wrq->u.sens);
 		if (! err)
 			changed = 1;
 		break;
@@ -2975,45 +3121,45 @@
 
 	case SIOCSIWRTS:
 		DEBUG(1, "%s: SIOCSIWRTS\n", dev->name);
-		err = dldwd_ioctl_setrts(dev, &wrq->u.rts);
+		err = orinoco_ioctl_setrts(dev, &wrq->u.rts);
 		if (! err)
 			changed = 1;
 		break;
 
 	case SIOCSIWFRAG:
 		DEBUG(1, "%s: SIOCSIWFRAG\n", dev->name);
-		err = dldwd_ioctl_setfrag(dev, &wrq->u.frag);
+		err = orinoco_ioctl_setfrag(dev, &wrq->u.frag);
 		if (! err)
 			changed = 1;
 		break;
 
 	case SIOCGIWFRAG:
 		DEBUG(1, "%s: SIOCGIWFRAG\n", dev->name);
-		err = dldwd_ioctl_getfrag(dev, &wrq->u.frag);
+		err = orinoco_ioctl_getfrag(dev, &wrq->u.frag);
 		break;
 
 	case SIOCSIWRATE:
 		DEBUG(1, "%s: SIOCSIWRATE\n", dev->name);
-		err = dldwd_ioctl_setrate(dev, &wrq->u.bitrate);
+		err = orinoco_ioctl_setrate(dev, &wrq->u.bitrate);
 		if (! err)
 			changed = 1;
 		break;
 
 	case SIOCGIWRATE:
 		DEBUG(1, "%s: SIOCGIWRATE\n", dev->name);
-		err = dldwd_ioctl_getrate(dev, &wrq->u.bitrate);
+		err = orinoco_ioctl_getrate(dev, &wrq->u.bitrate);
 		break;
 
 	case SIOCSIWPOWER:
 		DEBUG(1, "%s: SIOCSIWPOWER\n", dev->name);
-		err = dldwd_ioctl_setpower(dev, &wrq->u.power);
+		err = orinoco_ioctl_setpower(dev, &wrq->u.power);
 		if (! err)
 			changed = 1;
 		break;
 
 	case SIOCGIWPOWER:
 		DEBUG(1, "%s: SIOCGIWPOWER\n", dev->name);
-		err = dldwd_ioctl_getpower(dev, &wrq->u.power);
+		err = orinoco_ioctl_getpower(dev, &wrq->u.power);
 		break;
 
 	case SIOCGIWTXPOW:
@@ -3033,44 +3179,44 @@
 
 	case SIOCGIWRETRY:
 		DEBUG(1, "%s: SIOCGIWRETRY\n", dev->name);
-		err = dldwd_ioctl_getretry(dev, &wrq->u.retry);
+		err = orinoco_ioctl_getretry(dev, &wrq->u.retry);
 		break;
 #endif /* WIRELESS_EXT > 10 */
 
 	case SIOCSIWSPY:
 		DEBUG(1, "%s: SIOCSIWSPY\n", dev->name);
 
-		err = dldwd_ioctl_setspy(dev, &wrq->u.data);
+		err = orinoco_ioctl_setspy(dev, &wrq->u.data);
 		break;
 
 	case SIOCGIWSPY:
 		DEBUG(1, "%s: SIOCGIWSPY\n", dev->name);
 
-		err = dldwd_ioctl_getspy(dev, &wrq->u.data);
+		err = orinoco_ioctl_getspy(dev, &wrq->u.data);
 		break;
 
 	case SIOCGIWPRIV:
 		DEBUG(1, "%s: SIOCGIWPRIV\n", dev->name);
 		if (wrq->u.data.pointer) {
 			struct iw_priv_args privtab[] = {
-				{ SIOCDEVPRIVATE + 0x0, 0, 0, "force_reset" },
-				{ SIOCDEVPRIVATE + 0x1, 0, 0, "card_reset" },
-				{ SIOCDEVPRIVATE + 0x2,
+				{ SIOCIWFIRSTPRIV + 0x0, 0, 0, "force_reset" },
+				{ SIOCIWFIRSTPRIV + 0x1, 0, 0, "card_reset" },
+				{ SIOCIWFIRSTPRIV + 0x2,
 				  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
 				  0, "set_port3" },
-				{ SIOCDEVPRIVATE + 0x3, 0,
+				{ SIOCIWFIRSTPRIV + 0x3, 0,
 				  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
 				  "get_port3" },
-				{ SIOCDEVPRIVATE + 0x4,
+				{ SIOCIWFIRSTPRIV + 0x4,
 				  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
 				  0, "set_preamble" },
-				{ SIOCDEVPRIVATE + 0x5, 0,
+				{ SIOCIWFIRSTPRIV + 0x5, 0,
 				  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
 				  "get_preamble" },
-				{ SIOCDEVPRIVATE + 0x6,
+				{ SIOCIWFIRSTPRIV + 0x6,
 				  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
 				  0, "set_ibssport" },
-				{ SIOCDEVPRIVATE + 0x7, 0,
+				{ SIOCIWFIRSTPRIV + 0x7, 0,
 				  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
 				  "get_ibssport" }
 			};
@@ -3085,8 +3231,8 @@
 		}
 		break;
 	       
-	case SIOCDEVPRIVATE + 0x0: /* force_reset */
-		DEBUG(1, "%s: SIOCDEVPRIVATE + 0x0 (force_reset)\n",
+	case SIOCIWFIRSTPRIV + 0x0: /* force_reset */
+		DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x0 (force_reset)\n",
 		      dev->name);
 		if (! capable(CAP_NET_ADMIN)) {
 			err = -EPERM;
@@ -3094,11 +3240,11 @@
 		}
 		
 		printk(KERN_DEBUG "%s: Forcing reset!\n", dev->name);
-		dldwd_reset(priv);
+		orinoco_reset(priv);
 		break;
 
-	case SIOCDEVPRIVATE + 0x1: /* card_reset */
-		DEBUG(1, "%s: SIOCDEVPRIVATE + 0x1 (card_reset)\n",
+	case SIOCIWFIRSTPRIV + 0x1: /* card_reset */
+		DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x1 (card_reset)\n",
 		      dev->name);
 		if (! capable(CAP_NET_ADMIN)) {
 			err = -EPERM;
@@ -3108,30 +3254,30 @@
 		printk(KERN_DEBUG "%s: Forcing card reset!\n", dev->name);
 		if(priv->card_reset_handler != NULL)
 			priv->card_reset_handler(priv);
-		dldwd_reset(priv);
+		orinoco_reset(priv);
 		break;
 
-	case SIOCDEVPRIVATE + 0x2: /* set_port3 */
-		DEBUG(1, "%s: SIOCDEVPRIVATE + 0x2 (set_port3)\n",
+	case SIOCIWFIRSTPRIV + 0x2: /* set_port3 */
+		DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x2 (set_port3)\n",
 		      dev->name);
 		if (! capable(CAP_NET_ADMIN)) {
 			err = -EPERM;
 			break;
 		}
 
-		err = dldwd_ioctl_setport3(dev, wrq);
+		err = orinoco_ioctl_setport3(dev, wrq);
 		if (! err)
 			changed = 1;
 		break;
 
-	case SIOCDEVPRIVATE + 0x3: /* get_port3 */
-		DEBUG(1, "%s: SIOCDEVPRIVATE + 0x3 (get_port3)\n",
+	case SIOCIWFIRSTPRIV + 0x3: /* get_port3 */
+		DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x3 (get_port3)\n",
 		      dev->name);
-		err = dldwd_ioctl_getport3(dev, wrq);
+		err = orinoco_ioctl_getport3(dev, wrq);
 		break;
 
-	case SIOCDEVPRIVATE + 0x4: /* set_preamble */
-		DEBUG(1, "%s: SIOCDEVPRIVATE + 0x4 (set_preamble)\n",
+	case SIOCIWFIRSTPRIV + 0x4: /* set_preamble */
+		DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x4 (set_preamble)\n",
 		      dev->name);
 		if (! capable(CAP_NET_ADMIN)) {
 			err = -EPERM;
@@ -3146,46 +3292,46 @@
 		if(priv->has_preamble) {
 			int val = *( (int *) wrq->u.name );
 
-			dldwd_lock(priv);
+			orinoco_lock(priv);
 			if(val)
 				priv->preamble = 1;
 			else
 				priv->preamble = 0;
-			dldwd_unlock(priv);
+			orinoco_unlock(priv);
 			changed = 1;
 		} else
 			err = -EOPNOTSUPP;
 		break;
 
-	case SIOCDEVPRIVATE + 0x5: /* get_preamble */
-		DEBUG(1, "%s: SIOCDEVPRIVATE + 0x5 (get_preamble)\n",
+	case SIOCIWFIRSTPRIV + 0x5: /* get_preamble */
+		DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x5 (get_preamble)\n",
 		      dev->name);
 		if(priv->has_preamble) {
 			int *val = (int *)wrq->u.name;
 
-			dldwd_lock(priv);
+			orinoco_lock(priv);
 			*val = priv->preamble;
-			dldwd_unlock(priv);
+			orinoco_unlock(priv);
 		} else
 			err = -EOPNOTSUPP;
 		break;
-	case SIOCDEVPRIVATE + 0x6: /* set_ibssport */
-		DEBUG(1, "%s: SIOCDEVPRIVATE + 0x6 (set_ibssport)\n",
+	case SIOCIWFIRSTPRIV + 0x6: /* set_ibssport */
+		DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x6 (set_ibssport)\n",
 		      dev->name);
 		if (! capable(CAP_NET_ADMIN)) {
 			err = -EPERM;
 			break;
 		}
 
-		err = dldwd_ioctl_setibssport(dev, wrq);
+		err = orinoco_ioctl_setibssport(dev, wrq);
 		if (! err)
 			changed = 1;
 		break;
 
-	case SIOCDEVPRIVATE + 0x7: /* get_ibssport */
-		DEBUG(1, "%s: SIOCDEVPRIVATE + 0x7 (get_ibssport)\n",
+	case SIOCIWFIRSTPRIV + 0x7: /* get_ibssport */
+		DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x7 (get_ibssport)\n",
 		      dev->name);
-		err = dldwd_ioctl_getibssport(dev, wrq);
+		err = orinoco_ioctl_getibssport(dev, wrq);
 		break;
 
 
@@ -3194,14 +3340,13 @@
 	}
 	
 	if (! err && changed && netif_running(dev)) {
-		err = dldwd_reset(priv);
+		err = orinoco_reset(priv);
 		if (err) {
 			/* Ouch ! What are we supposed to do ? */
 			printk(KERN_ERR "orinoco_cs: Failed to set parameters on %s\n",
 			       dev->name);
-			netif_stop_queue(dev);
-			dldwd_shutdown(priv);
-			priv->hw_ready = 0;
+			netif_device_detach(dev);
+			orinoco_shutdown(priv);
 		}
 	}		
 
@@ -3211,11 +3356,11 @@
 }
 
 int
-dldwd_change_mtu(struct net_device *dev, int new_mtu)
+orinoco_change_mtu(struct net_device *dev, int new_mtu)
 {
 	TRACE_ENTER(dev->name);
 
-	if ( (new_mtu < DLDWD_MIN_MTU) || (new_mtu > DLDWD_MAX_MTU) )
+	if ( (new_mtu < ORINOCO_MIN_MTU) || (new_mtu > ORINOCO_MAX_MTU) )
 		return -EINVAL;
 
 	dev->mtu = new_mtu;
@@ -3226,19 +3371,19 @@
 }
 
 static void
-__dldwd_set_multicast_list(struct net_device *dev)
+__orinoco_set_multicast_list(struct net_device *dev)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	hermes_t *hw = &priv->hw;
 	int err = 0;
-	int promisc, allmulti, mc_count;
+	int promisc, mc_count;
 
 	/* We'll wait until it's ready. Anyway, the network doesn't call us
 	 * here until we are open - Jean II */
-	if (!priv->hw_ready)
+	/* FIXME: do we need this test at all? */
+	if (! netif_device_present(dev))
 		return;
 
-
 	TRACE_ENTER(dev->name);
 
 	DEBUG(3, "dev->flags=0x%x, priv->promiscuous=%d, dev->mc_count=%d priv->mc_count=%d\n",
@@ -3246,88 +3391,57 @@
 
 	/* The Hermes doesn't seem to have an allmulti mode, so we go
 	 * into promiscuous mode and let the upper levels deal. */
-	if ( (dev->flags & IFF_PROMISC) ) {
+	if ( (dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI) ||
+	     (dev->mc_count > HERMES_MAX_MULTICAST) ) {
 		promisc = 1;
-		allmulti = 0;
 		mc_count = 0;
-	} else if ( (dev->flags & IFF_ALLMULTI) ||
-		    (dev->mc_count > HERMES_MAX_MULTICAST) ) {
-		promisc = 0;
-		allmulti = 1;
-		mc_count = HERMES_MAX_MULTICAST;
 	} else {
 		promisc = 0;
-		allmulti = 0;
 		mc_count = dev->mc_count;
 	}
 
-	DEBUG(3, "promisc=%d mc_count=%d\n",
-	      promisc, mc_count);
-
-	if (promisc != priv->promiscuous) { /* Don't touch the hardware if we don't have to */
-		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_PROMISCUOUS,
+	if (promisc != priv->promiscuous) {
+		err = hermes_write_wordrec(hw, USER_BAP,
+					   HERMES_RID_CNFPROMISCUOUSMODE,
 					   promisc);
 		if (err) {
-			printk(KERN_ERR "%s: Error %d setting promiscuity to %d.\n",
-			       dev->name, err, promisc);
+			printk(KERN_ERR "%s: Error %d setting PROMISCUOUSMODE to 1.\n",
+			       dev->name, err);
 		} else 
 			priv->promiscuous = promisc;
-	}
 
-	if (allmulti) {
-		/* FIXME: This method of doing allmulticast reception
-		   comes from the NetBSD driver. Haven't actually
-		   tested whether it works or not. */
-		hermes_multicast_t mclist;
+		mc_count = 0;
+	}
 
-		memset(&mclist, 0, sizeof(mclist));
-		err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNF_MULTICAST_LIST, &mclist);
-		if (err)
-			printk(KERN_ERR "%s: Error %d setting multicast list.\n",
-			       dev->name, err);
-		else
-			priv->allmulti = 1;
-		       
-	} else if (mc_count || (! mc_count && priv->mc_count) ) {
+	if (mc_count || priv->mc_count) {
 		struct dev_mc_list *p = dev->mc_list;
 		hermes_multicast_t mclist;
 		int i;
 
 		for (i = 0; i < mc_count; i++) {
-			/* First some paranoid checks */
-			if (! p) {
-				printk(KERN_ERR "%s: Multicast list shorter than mc_count.\n",
-				       dev->name);
-				break;
-			}
-			if (p->dmi_addrlen != ETH_ALEN) {
-
-				printk(KERN_ERR "%s: Bad address size (%d) in multicast list.\n",
-				       dev->name, p->dmi_addrlen);
-				break;
-			}
-
+			/* Paranoia: */
+			if (! p)
+				BUG(); /* Multicast list shorter than mc_count */
+			if (p->dmi_addrlen != ETH_ALEN)
+				BUG(); /* Bad address size in multicast list */
+			
 			memcpy(mclist.addr[i], p->dmi_addr, ETH_ALEN);
 			p = p->next;
 		}
-
+		
 		/* More paranoia */
-		if (p)
-			printk(KERN_ERR "%s: Multicast list longer than mc_count.\n",
-			       dev->name);
-
-		priv->mc_count = i;			
-
-		DEBUG(3, "priv->mc_count = %d\n", priv->mc_count);
-
-		err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNF_MULTICAST_LIST,
+		if (p != dev->mc_list) {
+			printk(KERN_ERR "Multicast list is longer than mc_count\n");
+		}
+		
+		err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFGROUPADDRESSES,
 				       HERMES_BYTES_TO_RECLEN(priv->mc_count * ETH_ALEN),
 				       &mclist);
 		if (err)
 			printk(KERN_ERR "%s: Error %d setting multicast list.\n",
 			       dev->name, err);
 		else
-			priv->allmulti = 0;
+			priv->mc_count = mc_count;
 	}
 
 	/* Since we can set the promiscuous flag when it wasn't asked
@@ -3337,11 +3451,6 @@
 	else
 		dev->flags &= ~IFF_PROMISC;
 
-	if (priv->allmulti)
-		dev->flags |= IFF_ALLMULTI;
-	else
-		dev->flags &= ~IFF_ALLMULTI;
-
 	TRACE_EXIT(dev->name);
 }
 
@@ -3420,16 +3529,17 @@
 }
 
 static int
-dldwd_proc_get_hermes_regs(char *page, char **start, off_t requested_offset,
+orinoco_proc_get_hermes_regs(char *page, char **start, off_t requested_offset,
 			   int requested_len, int *eof, void *data)
 {
-	dldwd_priv_t *dev = (dldwd_priv_t *)data;
-	hermes_t *hw = &dev->hw;
+	struct orinoco_private *priv = (struct orinoco_private *)data;
+	struct net_device *dev = &priv->ndev;
+	hermes_t *hw = &priv->hw;
 	char *buf;
 	int total = 0, slop = 0;
 
 	/* Hum, in this case hardware register are probably not readable... */
-	if (!dev->hw_ready)
+	if (! netif_device_present(dev))
 		return -ENODEV;
 
 	buf = page;
@@ -3478,57 +3588,130 @@
 #define DISPLAY_WORDS	0
 #define DISPLAY_BYTES	1
 #define DISPLAY_STRING	2
+#define DISPLAY_XSTRING	3
 } record_table[] = {
-#define RTCNFENTRY(name, type) { HERMES_RID_CNF_##name, #name, 0, LTV_BUF_SIZE, type }
-	RTCNFENTRY(PORTTYPE, DISPLAY_WORDS),
-	RTCNFENTRY(MACADDR, DISPLAY_BYTES),
-	RTCNFENTRY(DESIRED_SSID, DISPLAY_STRING),
-	RTCNFENTRY(CHANNEL, DISPLAY_WORDS),
-	RTCNFENTRY(OWN_SSID, DISPLAY_STRING),
-	RTCNFENTRY(SYSTEM_SCALE, DISPLAY_WORDS),
-	RTCNFENTRY(MAX_DATA_LEN, DISPLAY_WORDS),
-	RTCNFENTRY(PM_ENABLE, DISPLAY_WORDS),
-	RTCNFENTRY(PM_MCAST_RX, DISPLAY_WORDS),
-	RTCNFENTRY(PM_PERIOD, DISPLAY_WORDS),
-	RTCNFENTRY(NICKNAME, DISPLAY_STRING),
-	RTCNFENTRY(WEP_ON, DISPLAY_WORDS),
-	RTCNFENTRY(MWO_ROBUST, DISPLAY_WORDS),
-	RTCNFENTRY(MULTICAST_LIST, DISPLAY_BYTES),
-	RTCNFENTRY(CREATEIBSS, DISPLAY_WORDS),
-	RTCNFENTRY(FRAG_THRESH, DISPLAY_WORDS),
-	RTCNFENTRY(RTS_THRESH, DISPLAY_WORDS),
-	RTCNFENTRY(TX_RATE_CTRL, DISPLAY_WORDS),
-	RTCNFENTRY(PROMISCUOUS, DISPLAY_WORDS),
-	RTCNFENTRY(KEYS, DISPLAY_BYTES),
-	RTCNFENTRY(TX_KEY, DISPLAY_WORDS),
-	RTCNFENTRY(TICKTIME, DISPLAY_WORDS),
-	RTCNFENTRY(INTERSIL_TX_KEY, DISPLAY_WORDS),
-	RTCNFENTRY(INTERSIL_KEY0, DISPLAY_BYTES),
-	RTCNFENTRY(INTERSIL_KEY1, DISPLAY_BYTES),
-	RTCNFENTRY(INTERSIL_KEY2, DISPLAY_BYTES),
-	RTCNFENTRY(INTERSIL_KEY3, DISPLAY_BYTES),
-	RTCNFENTRY(INTERSIL_WEP_ON, DISPLAY_WORDS),
-#undef RTCNFENTRY
-#define RTINFENTRY(name,type) { HERMES_RID_##name, #name, 0, LTV_BUF_SIZE, type }
-	RTINFENTRY(CHANNEL_LIST, DISPLAY_WORDS),
-	RTINFENTRY(STAIDENTITY, DISPLAY_WORDS),
-	RTINFENTRY(CURRENT_SSID, DISPLAY_STRING),
-	RTINFENTRY(CURRENT_BSSID, DISPLAY_BYTES),
-	RTINFENTRY(COMMSQUALITY, DISPLAY_WORDS),
-	RTINFENTRY(CURRENT_TX_RATE, DISPLAY_WORDS),
-	RTINFENTRY(WEP_AVAIL, DISPLAY_WORDS),
-	RTINFENTRY(CURRENT_CHANNEL, DISPLAY_WORDS),
-	RTINFENTRY(DATARATES, DISPLAY_BYTES),
-#undef RTINFENTRY
+#define CNF_WORDS(name) { HERMES_RID_CNF##name, #name, 0, LTV_BUF_SIZE, DISPLAY_WORDS }
+#define CNF_BYTES(name) { HERMES_RID_CNF##name, #name, 0, LTV_BUF_SIZE, DISPLAY_BYTES }
+#define CNF_STRING(name) { HERMES_RID_CNF##name, #name, 0, LTV_BUF_SIZE, DISPLAY_STRING }
+	CNF_WORDS(PORTTYPE),
+	CNF_BYTES(OWNMACADDR),
+	CNF_STRING(DESIREDSSID),
+	CNF_WORDS(OWNCHANNEL),
+	CNF_STRING(OWNSSID),
+	CNF_WORDS(OWNATIMWINDOW),
+	CNF_WORDS(SYSTEMSCALE),
+	CNF_WORDS(MAXDATALEN),
+	CNF_WORDS(PMENABLED),
+	CNF_WORDS(PMEPS),
+	CNF_WORDS(MULTICASTRECEIVE),
+	CNF_WORDS(MAXSLEEPDURATION),
+	CNF_WORDS(PMHOLDOVERDURATION),
+	CNF_STRING(OWNNAME),
+	CNF_WORDS(OWNDTIMPERIOD),
+	CNF_WORDS(MULTICASTPMBUFFERING),
+	CNF_WORDS(WEPENABLED_AGERE),
+	CNF_WORDS(MANDATORYBSSID_SYMBOL),
+	CNF_WORDS(WEPDEFAULTKEYID),
+	CNF_BYTES(DEFAULTKEY0),
+	CNF_BYTES(DEFAULTKEY1),
+	CNF_WORDS(MWOROBUST_AGERE),
+	CNF_BYTES(DEFAULTKEY2),
+	CNF_BYTES(DEFAULTKEY3),
+	CNF_WORDS(WEPFLAGS_INTERSIL),
+	CNF_WORDS(WEPKEYMAPPINGTABLE),
+	CNF_WORDS(AUTHENTICATION),
+	CNF_WORDS(MAXASSOCSTA),
+	CNF_WORDS(KEYLENGTH_SYMBOL),
+	CNF_WORDS(TXCONTROL),
+	CNF_WORDS(ROAMINGMODE),
+	CNF_WORDS(HOSTAUTHENTICATION),
+	CNF_WORDS(RCVCRCERROR),
+	CNF_WORDS(MMLIFE),
+	CNF_WORDS(ALTRETRYCOUNT),
+	CNF_WORDS(BEACONINT),
+	CNF_WORDS(APPCFINFO),
+	CNF_WORDS(STAPCFINFO),
+	CNF_WORDS(PRIORITYQUSAGE),
+	CNF_WORDS(TIMCTRL),
+	CNF_WORDS(THIRTY2TALLY),
+	CNF_WORDS(ENHSECURITY),
+	CNF_BYTES(GROUPADDRESSES),
+	CNF_WORDS(CREATEIBSS),
+	CNF_WORDS(FRAGMENTATIONTHRESHOLD),
+	CNF_WORDS(RTSTHRESHOLD),
+	CNF_WORDS(TXRATECONTROL),
+	CNF_WORDS(PROMISCUOUSMODE),
+	CNF_WORDS(BASICRATES_SYMBOL),
+	CNF_WORDS(PREAMBLE_SYMBOL),
+	CNF_WORDS(SHORTPREAMBLE),
+	CNF_BYTES(WEPKEYS_AGERE),
+	CNF_WORDS(EXCLUDELONGPREAMBLE),
+	CNF_WORDS(TXKEY_AGERE),
+	CNF_WORDS(AUTHENTICATIONRSPTO),
+	CNF_WORDS(BASICRATES),
+	CNF_WORDS(SUPPORTEDRATES),
+	CNF_WORDS(TICKTIME),
+	CNF_WORDS(SCANREQUEST),
+	CNF_WORDS(JOINREQUEST),
+	CNF_WORDS(AUTHENTICATESTATION),
+	CNF_WORDS(CHANNELINFOREQUEST),
+#undef CNF_WORDS
+#undef CNF_BYTES
+#undef CNF_STRING
+#define INF_WORDS(name) { HERMES_RID_##name, #name, 0, LTV_BUF_SIZE, DISPLAY_WORDS }
+#define INF_BYTES(name) { HERMES_RID_##name, #name, 0, LTV_BUF_SIZE, DISPLAY_BYTES }
+#define INF_STRING(name) { HERMES_RID_##name, #name, 0, LTV_BUF_SIZE, DISPLAY_STRING }
+#define INF_XSTRING(name) { HERMES_RID_##name, #name, 0, LTV_BUF_SIZE, DISPLAY_XSTRING }
+	INF_WORDS(MAXLOADTIME),
+	INF_WORDS(DOWNLOADBUFFER),
+	INF_WORDS(PRIID),
+	INF_WORDS(PRISUPRANGE),
+	INF_WORDS(CFIACTRANGES),
+	INF_WORDS(NICSERNUM),
+	INF_WORDS(NICID),
+	INF_WORDS(MFISUPRANGE),
+	INF_WORDS(CFISUPRANGE),
+	INF_WORDS(CHANNELLIST),
+	INF_WORDS(REGULATORYDOMAINS),
+	INF_WORDS(TEMPTYPE),
+/*  	INF_BYTES(CIS), */
+	INF_WORDS(STAID),
+	INF_STRING(CURRENTSSID),
+	INF_BYTES(CURRENTBSSID),
+	INF_WORDS(COMMSQUALITY),
+	INF_WORDS(CURRENTTXRATE),
+	INF_WORDS(CURRENTBEACONINTERVAL),
+	INF_WORDS(CURRENTSCALETHRESHOLDS),
+	INF_WORDS(PROTOCOLRSPTIME),
+	INF_WORDS(SHORTRETRYLIMIT),
+	INF_WORDS(LONGRETRYLIMIT),
+	INF_WORDS(MAXTRANSMITLIFETIME),
+	INF_WORDS(MAXRECEIVELIFETIME),
+	INF_WORDS(CFPOLLABLE),
+	INF_WORDS(AUTHENTICATIONALGORITHMS),
+	INF_WORDS(PRIVACYOPTIONIMPLEMENTED),
+	INF_BYTES(OWNMACADDR),
+	INF_WORDS(SCANRESULTSTABLE),
+	INF_WORDS(PHYTYPE),
+	INF_WORDS(CURRENTCHANNEL),
+	INF_WORDS(CURRENTPOWERSTATE),
+	INF_WORDS(CCAMODE),
+	INF_WORDS(SUPPORTEDDATARATES),
+	INF_BYTES(BUILDSEQ),
+	INF_XSTRING(FWID)
+#undef INF_WORDS
+#undef INF_BYTES
+#undef INF_STRING
 };
 #define NUM_RIDS ( sizeof(record_table) / sizeof(record_table[0]) )
 
 static int
-dldwd_proc_get_hermes_recs(char *page, char **start, off_t requested_offset,
+orinoco_proc_get_hermes_recs(char *page, char **start, off_t requested_offset,
 			   int requested_len, int *eof, void *data)
 {
-	dldwd_priv_t *dev = (dldwd_priv_t *)data;
-	hermes_t *hw = &dev->hw;
+	struct orinoco_private *priv = (struct orinoco_private *)data;
+	struct net_device *dev = &priv->ndev;
+	hermes_t *hw = &priv->hw;
 	char *buf;
 	int total = 0, slop = 0;
 	int i;
@@ -3536,7 +3719,7 @@
 	int err;
 
 	/* Hum, in this case hardware register are probably not readable... */
-	if (!dev->hw_ready)
+	if (! netif_device_present(dev))
 		return -ENODEV;
 		
 	buf = page;
@@ -3554,6 +3737,7 @@
 		val8 = kmalloc(maxlen + 2, GFP_KERNEL);
 		if (! val8)
 			return -ENOMEM;
+		memset(val8, 0, maxlen + 2);
 
 		err = hermes_read_ltv(hw, USER_BAP, rid, maxlen,
 				      &length, val8);
@@ -3562,6 +3746,8 @@
 			continue;
 		}
 		val16 = (u16 *)val8;
+		if (length == 0)
+			continue;
 
 		buf += sprintf(buf, "%-15s (0x%04x): length=%d (%d bytes)\tvalue=", record_table[i].name,
 			       rid, length, (length-1)*2);
@@ -3588,6 +3774,9 @@
 			val8[len] = '\0';
 			buf += sprintf(buf, "\"%s\"", (char *)&val16[1]);
 			break;
+		case DISPLAY_XSTRING:
+			
+			buf += sprintf(buf, "'%s'", (char *)val8);
 		}
 
 		buf += sprintf(buf, "\n");
@@ -3609,65 +3798,67 @@
 /* initialise the /proc subsystem for the hermes driver, creating the
  * separate entries */
 static int
-dldwd_proc_init(void)
+orinoco_proc_init(void)
 {
 	int err = 0;
 
-	TRACE_ENTER("dldwd");
+	TRACE_ENTER("orinoco");
 
 	/* create the directory for it to sit in */
 	dir_base = create_proc_entry("hermes", S_IFDIR, &proc_root);
 	if (dir_base == NULL) {
 		printk(KERN_ERR "Unable to initialise /proc/hermes.\n");
-		dldwd_proc_cleanup();
+		orinoco_proc_cleanup();
 		err = -ENOMEM;
 	}
 
-	TRACE_EXIT("dldwd");
+	TRACE_EXIT("orinoco");
 
 	return err;
 }
 
 int
-dldwd_proc_dev_init(dldwd_priv_t *dev)
+orinoco_proc_dev_init(struct orinoco_private *priv)
 {
-	struct net_device *ndev = &dev->ndev;
+	struct net_device *dev = &priv->ndev;
 
-	dev->dir_dev = NULL;
+	priv->dir_dev = NULL;
 	/* create the directory for it to sit in */
-	dev->dir_dev = create_proc_entry(ndev->name, S_IFDIR | S_IRUGO | S_IXUGO,
-					 dir_base);
-	if (dev->dir_dev == NULL) {
-		printk(KERN_ERR "Unable to initialise /proc/hermes/%s.\n",  ndev->name);
+	priv->dir_dev = create_proc_entry(dev->name, S_IFDIR | S_IRUGO | S_IXUGO,
+					  dir_base);
+	if (priv->dir_dev == NULL) {
+		printk(KERN_ERR "Unable to initialise /proc/hermes/%s.\n",  dev->name);
 		goto fail;
 	}
 
-	dev->dir_regs = NULL;
-	dev->dir_regs = create_proc_read_entry("regs", S_IFREG | S_IRUGO,
-					       dev->dir_dev, dldwd_proc_get_hermes_regs, dev);
-	if (dev->dir_regs == NULL) {
-		printk(KERN_ERR "Unable to initialise /proc/hermes/%s/regs.\n",  ndev->name);
+	priv->dir_regs = NULL;
+	priv->dir_regs = create_proc_read_entry("regs", S_IFREG | S_IRUGO,
+						priv->dir_dev, orinoco_proc_get_hermes_regs, priv);
+	if (priv->dir_regs == NULL) {
+		printk(KERN_ERR "Unable to initialise /proc/hermes/%s/regs.\n",  dev->name);
 		goto fail;
 	}
 
-	dev->dir_recs = NULL;
-	dev->dir_recs = create_proc_read_entry("recs", S_IFREG | S_IRUGO,
-					       dev->dir_dev, dldwd_proc_get_hermes_recs, dev);
-	if (dev->dir_recs == NULL) {
-		printk(KERN_ERR "Unable to initialise /proc/hermes/%s/recs.\n",  ndev->name);
+	priv->dir_recs = NULL;
+	priv->dir_recs = create_proc_read_entry("recs", S_IFREG | S_IRUGO,
+						priv->dir_dev, orinoco_proc_get_hermes_recs, priv);
+	if (priv->dir_recs == NULL) {
+		printk(KERN_ERR "Unable to initialise /proc/hermes/%s/recs.\n",  dev->name);
 		goto fail;
 	}
 
 	return 0;
  fail:
-	dldwd_proc_dev_cleanup(dev);
+	orinoco_proc_dev_cleanup(priv);
 	return -ENOMEM;
 }
 
 void
-dldwd_proc_dev_cleanup(dldwd_priv_t *priv)
+orinoco_proc_dev_cleanup(struct orinoco_private *priv)
 {
-	struct net_device *ndev = &priv->ndev;
+	struct net_device *dev = &priv->ndev;
+
+	TRACE_ENTER(priv->ndev.name);
 
 	if (priv->dir_regs) {
 		remove_proc_entry("regs", priv->dir_dev);
@@ -3678,26 +3869,28 @@
 		priv->dir_recs = NULL;
 	}		
 	if (priv->dir_dev) {
-		remove_proc_entry(ndev->name, dir_base);
+		remove_proc_entry(dev->name, dir_base);
 		priv->dir_dev = NULL;
 	}
+
+	TRACE_EXIT(priv->ndev.name);
 }
 
 static void
-dldwd_proc_cleanup(void)
+orinoco_proc_cleanup(void)
 {
-	TRACE_ENTER("dldwd");
+	TRACE_ENTER("orinoco");
 
 	if (dir_base) {
 		remove_proc_entry("hermes", &proc_root);
 		dir_base = NULL;
 	}
 	
-	TRACE_EXIT("dldwd");
+	TRACE_EXIT("orinoco");
 }
 
 int
-dldwd_setup(dldwd_priv_t* priv)
+orinoco_setup(struct orinoco_private* priv)
 {
 	struct net_device *dev = &priv->ndev;;
 
@@ -3709,20 +3902,20 @@
 
 	/* Setup up default routines */
 	priv->card_reset_handler = NULL;	/* Caller may override */
-	dev->init = dldwd_init;
+	dev->init = orinoco_init;
 	dev->open = NULL;		/* Caller *must* override */
 	dev->stop = NULL;
-	dev->hard_start_xmit = dldwd_xmit;
-	dev->tx_timeout = dldwd_tx_timeout;
-	dev->watchdog_timeo = HZ; /* 4 second timeout */
+	dev->hard_start_xmit = orinoco_xmit;
+	dev->tx_timeout = orinoco_tx_timeout;
+	dev->watchdog_timeo = HZ; /* 1 second timeout */
 
-	dev->get_stats = dldwd_get_stats;
-	dev->get_wireless_stats = dldwd_get_wireless_stats;
+	dev->get_stats = orinoco_get_stats;
+	dev->get_wireless_stats = orinoco_get_wireless_stats;
 
-	dev->do_ioctl = dldwd_ioctl;
+	dev->do_ioctl = orinoco_ioctl;
 
-	dev->change_mtu = dldwd_change_mtu;
-	dev->set_multicast_list = dldwd_set_multicast_list;
+	dev->change_mtu = orinoco_change_mtu;
+	dev->set_multicast_list = orinoco_set_multicast_list;
 
 	netif_stop_queue(dev);
 
@@ -3730,36 +3923,31 @@
 }
 
 #ifdef ORINOCO_DEBUG
-EXPORT_SYMBOL(dldwd_debug);
+EXPORT_SYMBOL(orinoco_debug);
 #endif
-EXPORT_SYMBOL(dldwd_init);
-EXPORT_SYMBOL(dldwd_xmit);
-EXPORT_SYMBOL(dldwd_tx_timeout);
-EXPORT_SYMBOL(dldwd_ioctl);
-EXPORT_SYMBOL(dldwd_change_mtu);
-EXPORT_SYMBOL(dldwd_set_multicast_list);
-EXPORT_SYMBOL(dldwd_shutdown);
-EXPORT_SYMBOL(dldwd_reset);
-EXPORT_SYMBOL(dldwd_setup);
-EXPORT_SYMBOL(dldwd_proc_dev_init);
-EXPORT_SYMBOL(dldwd_proc_dev_cleanup);
-EXPORT_SYMBOL(dldwd_interrupt);
+EXPORT_SYMBOL(orinoco_init);
+EXPORT_SYMBOL(orinoco_xmit);
+EXPORT_SYMBOL(orinoco_tx_timeout);
+EXPORT_SYMBOL(orinoco_ioctl);
+EXPORT_SYMBOL(orinoco_change_mtu);
+EXPORT_SYMBOL(orinoco_set_multicast_list);
+EXPORT_SYMBOL(orinoco_shutdown);
+EXPORT_SYMBOL(orinoco_reset);
+EXPORT_SYMBOL(orinoco_setup);
+EXPORT_SYMBOL(orinoco_proc_dev_init);
+EXPORT_SYMBOL(orinoco_proc_dev_cleanup);
+EXPORT_SYMBOL(orinoco_interrupt);
 
-static int __init init_dldwd(void)
+static int __init init_orinoco(void)
 {
-	int err;
-
-	err = dldwd_proc_init();
-
 	printk(KERN_DEBUG "%s\n", version);
-
-	return 0;
+	return orinoco_proc_init();
 }
 
-static void __exit exit_dldwd(void)
+static void __exit exit_orinoco(void)
 {
-	dldwd_proc_cleanup();
+	orinoco_proc_cleanup();
 }
 
-module_init(init_dldwd);
-module_exit(exit_dldwd);
+module_init(init_orinoco);
+module_exit(exit_orinoco);
diff -urN linux-2.4.18-pre2/drivers/net/wireless/orinoco.h linux/drivers/net/wireless/orinoco.h
--- linux-2.4.18-pre2/drivers/net/wireless/orinoco.h	Tue Oct  9 15:13:03 2001
+++ linux/drivers/net/wireless/orinoco.h	Thu Jan 10 13:28:35 2002
@@ -8,50 +8,34 @@
 #define _ORINOCO_H
 
 /* To enable debug messages */
-/*  #define ORINOCO_DEBUG		3 */
+// #define ORINOCO_DEBUG		3
 
 #if (! defined (WIRELESS_EXT)) || (WIRELESS_EXT < 10)
-#error "orinoco_cs requires Wireless extensions v10 or later."
+#error "orinoco driver requires Wireless extensions v10 or later."
 #endif /* (! defined (WIRELESS_EXT)) || (WIRELESS_EXT < 10) */
 #define WIRELESS_SPY		// enable iwspy support
 
+#define ORINOCO_MAX_KEY_SIZE	14
+#define ORINOCO_MAX_KEYS	4
 
-#define DLDWD_MIN_MTU		256
-#define DLDWD_MAX_MTU		(HERMES_FRAME_LEN_MAX - ENCAPS_OVERHEAD)
+typedef struct orinoco_key {
+	u16 len;	/* always store little-endian */
+	char data[ORINOCO_MAX_KEY_SIZE];
+} __attribute__ ((packed)) orinoco_key_t;
 
-#define LTV_BUF_SIZE		128
-#define USER_BAP		0
-#define IRQ_BAP			1
-#define DLDWD_MACPORT		0
-#define IRQ_LOOP_MAX		10
-#define TX_NICBUF_SIZE		2048
-#define TX_NICBUF_SIZE_BUG	1585		/* Bug in Symbol firmware */
-#define MAX_KEYS		4
-#define MAX_KEY_SIZE		14
-#define LARGE_KEY_SIZE		13
-#define SMALL_KEY_SIZE		5
-#define MAX_FRAME_SIZE		2304
-
-typedef struct dldwd_key {
-	uint16_t len;	/* always store little-endian */
-	char data[MAX_KEY_SIZE];
-} __attribute__ ((packed)) dldwd_key_t;
-
-typedef dldwd_key_t dldwd_keys_t[MAX_KEYS];
+typedef orinoco_key_t orinoco_keys_t[ORINOCO_MAX_KEYS];
 
 /*====================================================================*/
 
-
-typedef struct dldwd_priv {
+struct orinoco_private {
 	void* card;	/* Pointer to card dependant structure */
 	/* card dependant extra reset code (i.e. bus/interface specific */
-	int (*card_reset_handler)(struct dldwd_priv *);
+	int (*card_reset_handler)(struct orinoco_private *);
 
 	spinlock_t lock;
 	long state;
-#define DLDWD_STATE_INIRQ 0
-#define DLDWD_STATE_DOIRQ 1
-	int hw_ready;	/* HW may be suspended by platform */
+#define ORINOCO_STATE_INIRQ 0
+#define ORINOCO_STATE_DOIRQ 1
 
 	/* Net device stuff */
 	struct net_device ndev;
@@ -61,11 +45,11 @@
 
 	/* Hardware control variables */
 	hermes_t hw;
-	uint16_t txfid;
+	u16 txfid;
 
 	/* Capabilities of the hardware/firmware */
 	int firmware_type;
-#define FIRMWARE_TYPE_LUCENT 1
+#define FIRMWARE_TYPE_AGERE 1
 #define FIRMWARE_TYPE_INTERSIL 2
 #define FIRMWARE_TYPE_SYMBOL 3
 	int has_ibss, has_port3, prefer_port3, has_ibss_any, ibss_port;
@@ -74,23 +58,26 @@
 	int has_pm;
 	int has_preamble;
 	int need_card_reset, broken_reset, broken_allocate;
-	uint16_t channel_mask;
+	u16 channel_mask;
 
 	/* Current configuration */
-	uint32_t iw_mode;
+	u32 iw_mode;
 	int port_type, allow_ibss;
-	uint16_t wep_on, wep_restrict, tx_key;
-	dldwd_keys_t keys;
+
+	u16 wep_on, wep_restrict, tx_key;
+	orinoco_keys_t keys;
+
+	int bitratemode;
+
  	char nick[IW_ESSID_MAX_SIZE+1];
 	char desired_essid[IW_ESSID_MAX_SIZE+1];
-	uint16_t frag_thresh, mwo_robust;
-	uint16_t channel;
-	uint16_t ap_density, rts_thresh;
-	uint16_t tx_rate_ctrl;
-	uint16_t pm_on, pm_mcast, pm_period, pm_timeout;
-	uint16_t preamble;
+	u16 frag_thresh, mwo_robust;
+	u16 channel;
+	u16 ap_density, rts_thresh;
+	u16 pm_on, pm_mcast, pm_period, pm_timeout;
+	u16 preamble;
 
-	int promiscuous, allmulti, mc_count;
+	int promiscuous, mc_count;
 
 #ifdef WIRELESS_SPY
 	int			spy_number;
@@ -102,16 +89,16 @@
 	struct proc_dir_entry *dir_dev;
 	struct proc_dir_entry *dir_regs;
 	struct proc_dir_entry *dir_recs;
-} dldwd_priv_t;
+};
 
 /*====================================================================*/
 
-extern struct list_head dldwd_instances;
+extern struct list_head orinoco_instances;
 
 #ifdef ORINOCO_DEBUG
-extern int dldwd_debug;
-#define DEBUG(n, args...) do { if (dldwd_debug>(n)) printk(KERN_DEBUG args); } while(0)
-#define DEBUGMORE(n, args...) do { if (dldwd_debug>(n)) printk(args); } while (0)
+extern int orinoco_debug;
+#define DEBUG(n, args...) do { if (orinoco_debug>(n)) printk(KERN_DEBUG args); } while(0)
+#define DEBUGMORE(n, args...) do { if (orinoco_debug>(n)) printk(args); } while (0)
 #else
 #define DEBUG(n, args...) do { } while (0)
 #define DEBUGMORE(n, args...) do { } while (0)
@@ -123,20 +110,20 @@
 #define RUP_EVEN(a) ( (a) % 2 ? (a) + 1 : (a) )
 
 /* struct net_device methods */
-extern int dldwd_init(struct net_device *dev);
-extern int dldwd_xmit(struct sk_buff *skb, struct net_device *dev);
-extern void dldwd_tx_timeout(struct net_device *dev);
-
-extern int dldwd_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-extern int dldwd_change_mtu(struct net_device *dev, int new_mtu);
-extern void dldwd_set_multicast_list(struct net_device *dev);
+extern int orinoco_init(struct net_device *dev);
+extern int orinoco_xmit(struct sk_buff *skb, struct net_device *dev);
+extern void orinoco_tx_timeout(struct net_device *dev);
+
+extern int orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+extern int orinoco_change_mtu(struct net_device *dev, int new_mtu);
+extern void orinoco_set_multicast_list(struct net_device *dev);
 
 /* utility routines */
-extern void dldwd_shutdown(dldwd_priv_t *dev);
-extern int dldwd_reset(dldwd_priv_t *dev);
-extern int dldwd_setup(dldwd_priv_t* priv);
-extern int dldwd_proc_dev_init(dldwd_priv_t *dev);
-extern void dldwd_proc_dev_cleanup(dldwd_priv_t *priv);
-extern void dldwd_interrupt(int irq, void * dev_id, struct pt_regs *regs);
+extern void orinoco_shutdown(struct orinoco_private *dev);
+extern int orinoco_reset(struct orinoco_private *dev);
+extern int orinoco_setup(struct orinoco_private* priv);
+extern int orinoco_proc_dev_init(struct orinoco_private *dev);
+extern void orinoco_proc_dev_cleanup(struct orinoco_private *priv);
+extern void orinoco_interrupt(int irq, void * dev_id, struct pt_regs *regs);
 
-#endif
+#endif /* _ORINOCO_H */
diff -urN linux-2.4.18-pre2/drivers/net/wireless/orinoco_cs.c linux/drivers/net/wireless/orinoco_cs.c
--- linux-2.4.18-pre2/drivers/net/wireless/orinoco_cs.c	Tue Oct  9 15:13:03 2001
+++ linux/drivers/net/wireless/orinoco_cs.c	Thu Jan 10 13:28:35 2002
@@ -1,4 +1,4 @@
-/* orinoco_cs.c 0.08a	- (formerly known as dldwd_cs.c)
+/* orinoco_cs.c 0.09	- (formerly known as dldwd_cs.c)
  *
  * A driver for "Hermes" chipset based PCMCIA wireless adaptors, such
  * as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/
@@ -44,7 +44,7 @@
 
 /*====================================================================*/
 
-static char version[] __initdata = "orinoco_cs.c 0.08a (David Gibson <hermes@gibson.dropbear.id.au> and others)";
+static char version[] __initdata = "orinoco_cs.c 0.09 (David Gibson <hermes@gibson.dropbear.id.au> and others)";
 
 MODULE_AUTHOR("David Gibson <hermes@gibson.dropbear.id.au>");
 MODULE_DESCRIPTION("Driver for PCMCIA Lucent Orinoco, Prism II based and similar wireless cards");
@@ -68,33 +68,31 @@
 MODULE_PARM(reset_cor, "i");
 MODULE_PARM(ignore_cis_vcc, "i");
 
-
 /* Pcmcia specific structure */
-typedef struct dldwd_card {
+struct orinoco_pccard {
 	dev_link_t link;
 	dev_node_t node;
-	int instance;
 
 	/* Common structure (fully included), see orinoco.h */
-	struct dldwd_priv  priv;
-} dldwd_card_t;
+	struct orinoco_private  priv;
+};
 
 /*
  * Function prototypes
  */
 
 /* struct net_device methods */
-static int dldwd_cs_open(struct net_device *dev);
-static int dldwd_cs_stop(struct net_device *dev);
+static int orinoco_cs_open(struct net_device *dev);
+static int orinoco_cs_stop(struct net_device *dev);
 
 /* PCMCIA gumpf */
-static void dldwd_cs_config(dev_link_t * link);
-static void dldwd_cs_release(u_long arg);
-static int dldwd_cs_event(event_t event, int priority,
+static void orinoco_cs_config(dev_link_t * link);
+static void orinoco_cs_release(u_long arg);
+static int orinoco_cs_event(event_t event, int priority,
 		       event_callback_args_t * args);
 
-static dev_link_t *dldwd_cs_attach(void);
-static void dldwd_cs_detach(dev_link_t *);
+static dev_link_t *orinoco_cs_attach(void);
+static void orinoco_cs_detach(dev_link_t *);
 
 /*
    The dev_info variable is the "key" that is used to match up this
@@ -115,7 +113,6 @@
 */
 
 static dev_link_t *dev_list; /* = NULL */
-static int num_instances; /* = 0 */
 
 /*====================================================================*/
 
@@ -127,10 +124,10 @@
 }
 
 static int
-dldwd_cs_open(struct net_device *dev)
+orinoco_cs_open(struct net_device *dev)
 {
-	dldwd_priv_t *priv = (dldwd_priv_t *)dev->priv;
-	dldwd_card_t* card = (dldwd_card_t *)priv->card;
+	struct orinoco_private *priv = (struct orinoco_private *)dev->priv;
+	struct orinoco_pccard* card = (struct orinoco_pccard *)priv->card;
 	dev_link_t *link = &card->link;
 	int err;
 	
@@ -139,9 +136,9 @@
 	link->open++;
 	netif_device_attach(dev);
 	
-	err = dldwd_reset(priv);
+	err = orinoco_reset(priv);
 	if (err)
-		dldwd_cs_stop(dev);
+		orinoco_cs_stop(dev);
 	else
 		netif_start_queue(dev);
 
@@ -151,17 +148,17 @@
 }
 
 static int
-dldwd_cs_stop(struct net_device *dev)
+orinoco_cs_stop(struct net_device *dev)
 {
-	dldwd_priv_t *priv = (dldwd_priv_t *)dev->priv;
-	dldwd_card_t* card = (dldwd_card_t *)priv->card;
+	struct orinoco_private *priv = (struct orinoco_private *)dev->priv;
+	struct orinoco_pccard* card = (struct orinoco_pccard *)priv->card;
 	dev_link_t *link = &card->link;
 
 	TRACE_ENTER(priv->ndev.name);
 
 	netif_stop_queue(dev);
 
-	dldwd_shutdown(priv);
+	orinoco_shutdown(priv);
 	
 	link->open--;
 
@@ -179,9 +176,9 @@
  * In fact, this seem necessary for Spectrum cards...
  */
 static int
-dldwd_cs_cor_reset(dldwd_priv_t *priv)
+orinoco_cs_cor_reset(struct orinoco_private *priv)
 {
-	dldwd_card_t* card = (dldwd_card_t *)priv->card;
+	struct orinoco_pccard* card = (struct orinoco_pccard *)priv->card;
 	dev_link_t *link = &card->link;
 	conf_reg_t reg;
 	u_int default_cor; 
@@ -189,8 +186,8 @@
 	TRACE_ENTER(priv->ndev.name);
 
 	/* Doing it if hardware is gone is guaranteed crash */
-	if(!priv->hw_ready)
-		return(0);
+	if(! (link->state & DEV_CONFIG) )
+		return -ENODEV;
 
 	/* Save original COR value */
 	reg.Function = 0;
@@ -200,7 +197,7 @@
 	CardServices(AccessConfigurationRegister, link->handle, &reg);
 	default_cor = reg.Value;
 
-	DEBUG(2, "dldwd : dldwd_cs_cor_reset() : cor=0x%X\n", default_cor);
+	DEBUG(2, "orinoco : orinoco_cs_cor_reset() : cor=0x%X\n", default_cor);
 
 	/* Soft-Reset card */
 	reg.Action = CS_WRITE;
@@ -209,6 +206,7 @@
 	CardServices(AccessConfigurationRegister, link->handle, &reg);
 
 	/* Wait until the card has acknowledged our reset */
+	/* FIXME: mdelay() is deprecated -dgibson */
 	mdelay(1);
 
 	/* Restore original COR configuration index */
@@ -216,11 +214,12 @@
 	CardServices(AccessConfigurationRegister, link->handle, &reg);
 
 	/* Wait until the card has finished restarting */
+	/* FIXME: mdelay() is deprecated -dgibson */
 	mdelay(1);
 
 	TRACE_EXIT(priv->ndev.name);
 
-	return(0);
+	return 0;
 }
 
 /* Remove zombie instances (card removed, detach pending) */
@@ -228,17 +227,17 @@
 flush_stale_links(void)
 {
 	dev_link_t *link, *next;
-	TRACE_ENTER("dldwd");
+	TRACE_ENTER("orinoco");
 	for (link = dev_list; link; link = next) {
 		next = link->next;
 		if (link->state & DEV_STALE_LINK)
-			dldwd_cs_detach(link);
+			orinoco_cs_detach(link);
 	}
-	TRACE_EXIT("dldwd");
+	TRACE_EXIT("orinoco");
 }
 
 /*======================================================================
-  dldwd_cs_attach() creates an "instance" of the driver, allocating
+  orinoco_cs_attach() creates an "instance" of the driver, allocating
   local data structures for one device.  The device is registered
   with Card Services.
   
@@ -248,16 +247,16 @@
   ======================================================================*/
 
 static dev_link_t *
-dldwd_cs_attach(void)
+orinoco_cs_attach(void)
 {
-	dldwd_card_t *card;
-	dldwd_priv_t *priv;
+	struct orinoco_pccard *card;
+	struct orinoco_private *priv;
 	dev_link_t *link;
 	struct net_device *ndev;
 	client_reg_t client_reg;
 	int ret, i;
 
-	TRACE_ENTER("dldwd");
+	TRACE_ENTER("orinoco");
 	/* A bit of cleanup */
 	flush_stale_links();
 
@@ -272,13 +271,12 @@
 	/* Link both structure together */
 	priv = &(card->priv);
 	priv->card = card;
-	card->instance = num_instances++; /* FIXME: Racy? */
 	link = &card->link;
 	ndev = &priv->ndev;
 	link->priv = priv;
 
 	/* Initialize the dev_link_t structure */
-	link->release.function = &dldwd_cs_release;
+	link->release.function = &orinoco_cs_release;
 	link->release.data = (u_long) link;
 
 	/* Interrupt setup */
@@ -302,15 +300,15 @@
 	link->conf.IntType = INT_MEMORY_AND_IO;
 
 	/* Setup the common part */
-	if(dldwd_setup(priv) < 0) {
+	if(orinoco_setup(priv) < 0) {
 		kfree(card);
 		return NULL;
 	}
 
 	/* Overrides */
-	ndev->open = dldwd_cs_open;
-	ndev->stop = dldwd_cs_stop;
-	priv->card_reset_handler = dldwd_cs_cor_reset;
+	ndev->open = orinoco_cs_open;
+	ndev->stop = orinoco_cs_stop;
+	priv->card_reset_handler = orinoco_cs_cor_reset;
 
 	/* Register with Card Services */
 	link->next = dev_list;
@@ -321,21 +319,21 @@
 	    CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
 	    CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
 	    CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
-	client_reg.event_handler = &dldwd_cs_event;
+	client_reg.event_handler = &orinoco_cs_event;
 	client_reg.Version = 0x0210;
 	client_reg.event_callback_args.client_data = link;
 	ret = CardServices(RegisterClient, &link->handle, &client_reg);
 	if (ret != CS_SUCCESS) {
 		cs_error(link->handle, RegisterClient, ret);
-		dldwd_cs_detach(link);
+		orinoco_cs_detach(link);
 		link = NULL;
 		goto out;
 	}
 
  out:
-	TRACE_EXIT("dldwd");
+	TRACE_EXIT("orinoco");
 	return link;
-}				/* dldwd_cs_attach */
+}				/* orinoco_cs_attach */
 
 /*======================================================================
   This deletes a driver "instance".  The device is de-registered
@@ -345,12 +343,12 @@
   ======================================================================*/
 
 static void
-dldwd_cs_detach(dev_link_t * link)
+orinoco_cs_detach(dev_link_t * link)
 {
 	dev_link_t **linkp;
-	dldwd_priv_t *priv = link->priv;
+	struct orinoco_private *priv = link->priv;
 
-	TRACE_ENTER("dldwd");
+	TRACE_ENTER("orinoco");
 
 	/* Locate device structure */
 	for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
@@ -388,14 +386,12 @@
 	}
 	kfree(priv->card);
 
-	num_instances--; /* FIXME: Racy? */
-
  out:
-	TRACE_EXIT("dldwd");
-}				/* dldwd_cs_detach */
+	TRACE_EXIT("orinoco");
+}				/* orinoco_cs_detach */
 
 /*======================================================================
-  dldwd_cs_config() is scheduled to run after a CARD_INSERTION event
+  orinoco_cs_config() is scheduled to run after a CARD_INSERTION event
   is received, to configure the PCMCIA socket, and to make the
   device available to the system.
   ======================================================================*/
@@ -407,11 +403,11 @@
 if (CardServices(fn, args) != 0) goto next_entry
 
 static void
-dldwd_cs_config(dev_link_t * link)
+orinoco_cs_config(dev_link_t * link)
 {
 	client_handle_t handle = link->handle;
-	dldwd_priv_t *priv = link->priv;
-	dldwd_card_t *card = (dldwd_card_t *)priv->card;
+	struct orinoco_private *priv = link->priv;
+	struct orinoco_pccard *card = (struct orinoco_pccard *)priv->card;
 	hermes_t *hw = &priv->hw;
 	struct net_device *ndev = &priv->ndev;
 	tuple_t tuple;
@@ -422,7 +418,7 @@
 	cistpl_cftable_entry_t dflt = { 0 };
 	cisinfo_t info;
 
-	TRACE_ENTER("dldwd");
+	TRACE_ENTER("orinoco");
 
 	CS_CHECK(ValidateCIS, handle, &info);
 
@@ -448,7 +444,7 @@
 	CS_CHECK(GetConfigurationInfo, handle, &conf);
 	link->conf.Vcc = conf.Vcc;
 
-	DEBUG(0, "dldwd_cs_config: ConfigBase = 0x%x link->conf.Vcc = %d\n", 
+	DEBUG(0, "orinoco_cs_config: ConfigBase = 0x%x link->conf.Vcc = %d\n", 
 	      link->conf.ConfigBase, link->conf.Vcc);
 
 	/*
@@ -470,7 +466,7 @@
 		CFG_CHECK(GetTupleData, handle, &tuple);
 		CFG_CHECK(ParseTuple, handle, &tuple, &parse);
 
-		DEBUG(0, "dldwd_cs_config: index = 0x%x, flags = 0x%x\n",
+		DEBUG(0, "orinoco_cs_config: index = 0x%x, flags = 0x%x\n",
 		      cfg->index, cfg->flags);
 
 		if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
@@ -490,14 +486,14 @@
 		if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) {
 			if (conf.Vcc !=
 			    cfg->vcc.param[CISTPL_POWER_VNOM] / 10000) {
-				DEBUG(2, "dldwd_cs_config: Vcc mismatch (conf.Vcc = %d, CIS = %d)\n",  conf.Vcc, cfg->vcc.param[CISTPL_POWER_VNOM] / 10000);
+				DEBUG(2, "orinoco_cs_config: Vcc mismatch (conf.Vcc = %d, CIS = %d)\n",  conf.Vcc, cfg->vcc.param[CISTPL_POWER_VNOM] / 10000);
 				if(!ignore_cis_vcc)
 					goto next_entry;
 			}
 		} else if (dflt.vcc.present & (1 << CISTPL_POWER_VNOM)) {
 			if (conf.Vcc !=
 			    dflt.vcc.param[CISTPL_POWER_VNOM] / 10000) {
-				DEBUG(2, "dldwd_cs_config: Vcc mismatch (conf.Vcc = %d, CIS = %d)\n",  conf.Vcc, dflt.vcc.param[CISTPL_POWER_VNOM] / 10000);
+				DEBUG(2, "orinoco_cs_config: Vcc mismatch (conf.Vcc = %d, CIS = %d)\n",  conf.Vcc, dflt.vcc.param[CISTPL_POWER_VNOM] / 10000);
 				if(!ignore_cis_vcc)
 					goto next_entry;
 			}
@@ -510,7 +506,7 @@
 			link->conf.Vpp1 = link->conf.Vpp2 =
 			    dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000;
 		
-		DEBUG(0, "dldwd_cs_config: We seem to have configured Vcc and Vpp\n");
+		DEBUG(0, "orinoco_cs_config: We seem to have configured Vcc and Vpp\n");
 
 		/* Do we need to allocate an interrupt? */
 		if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
@@ -570,7 +566,7 @@
 			for (i=0; i<4; i++)
 				link->irq.IRQInfo2 |= 1 << irq_list[i];
 		
-  		link->irq.Handler = dldwd_interrupt; 
+  		link->irq.Handler = orinoco_interrupt; 
   		link->irq.Instance = priv; 
 		
 		CS_CHECK(RequestIRQ, link->handle, &link->irq);
@@ -591,6 +587,12 @@
 	ndev->base_addr = link->io.BasePort1;
 	ndev->irq = link->irq.AssignedIRQ;
 
+	/* Now do a PCMCIA soft reset on the card, to make sure its in
+	   a sane state */
+	/* Optional because it really mess up old Lucent firmwares - Jean II */
+	if (reset_cor)
+		orinoco_cs_cor_reset(priv);
+
 	/* register_netdev will give us an ethX name */
 	ndev->name[0] = '\0';
 	/* Tell the stack we exist */
@@ -618,7 +620,7 @@
 	printk("\n");
 
 	/* And give us the proc nodes for debugging */
-	if (dldwd_proc_dev_init(priv) != 0) {
+	if (orinoco_proc_dev_init(priv) != 0) {
 		printk(KERN_ERR "orinoco_cs: Failed to create /proc node for %s\n",
 		       ndev->name);
 		goto failed;
@@ -627,12 +629,9 @@
 	/* Note to myself : this replace MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT */
 	SET_MODULE_OWNER(ndev);
 	
-	/* Allow cor_reset, /proc & ioctls to act */
-	priv->hw_ready = 1;
-	
 	/* Do a Pcmcia soft reset of the card (optional) */
-	if(reset_cor)
-		dldwd_cs_cor_reset(priv);
+	if (reset_cor)
+		orinoco_cs_cor_reset(priv);
 
 	/*
 	   At this point, the dev_node_t structure(s) need to be
@@ -642,29 +641,29 @@
 	link->dev = &card->node;
 	link->state &= ~DEV_CONFIG_PENDING;
 
-	TRACE_EXIT("dldwd");
+	TRACE_EXIT("orinoco");
 
 	return;
 
  cs_failed:
 	cs_error(link->handle, last_fn, last_ret);
  failed:
-	dldwd_cs_release((u_long) link);
+	orinoco_cs_release((u_long) link);
 
-	TRACE_EXIT("dldwd");
-}				/* dldwd_cs_config */
+	TRACE_EXIT("orinoco");
+}				/* orinoco_cs_config */
 
 /*======================================================================
-  After a card is removed, dldwd_cs_release() will unregister the
+  After a card is removed, orinoco_cs_release() will unregister the
   device, and release the PCMCIA configuration.  If the device is
   still open, this will be postponed until it is closed.
   ======================================================================*/
 
 static void
-dldwd_cs_release(u_long arg)
+orinoco_cs_release(u_long arg)
 {
 	dev_link_t *link = (dev_link_t *) arg;
-	dldwd_priv_t *priv = link->priv;
+	struct orinoco_private *priv = link->priv;
 
 	TRACE_ENTER(link->dev->dev_name);
 
@@ -681,7 +680,7 @@
 	}
 
 	/* Unregister proc entry */
-	dldwd_proc_dev_cleanup(priv);
+	orinoco_proc_dev_cleanup(priv);
 
 	/* Don't bother checking to see if these succeed or not */
 	CardServices(ReleaseConfiguration, link->handle);
@@ -692,7 +691,7 @@
 	link->state &= ~DEV_CONFIG;
 
 	TRACE_EXIT(link->dev->dev_name);
-}				/* dldwd_cs_release */
+}				/* orinoco_cs_release */
 
 /*======================================================================
   The card status event handler.  Mostly, this schedules other
@@ -705,39 +704,37 @@
   ======================================================================*/
 
 static int
-dldwd_cs_event(event_t event, int priority,
+orinoco_cs_event(event_t event, int priority,
 		       event_callback_args_t * args)
 {
 	dev_link_t *link = args->client_data;
-	dldwd_priv_t *priv = (dldwd_priv_t *)link->priv;
+	struct orinoco_private *priv = (struct orinoco_private *)link->priv;
 	struct net_device *dev = &priv->ndev;
 
-	TRACE_ENTER("dldwd");
+	TRACE_ENTER("orinoco");
 
 	switch (event) {
 	case CS_EVENT_CARD_REMOVAL:
-		/* FIXME: Erg.. this whole hw_ready thing looks racy
-		   to me.  this may not be fixable without changin the
-		   PCMCIA subsystem, though */
-		priv->hw_ready = 0;
-		dldwd_shutdown(priv);
 		link->state &= ~DEV_PRESENT;
 		if (link->state & DEV_CONFIG) {
 			netif_stop_queue(dev);
+		}
+		orinoco_shutdown(priv);
+		if (link->state & DEV_CONFIG) {
 			netif_device_detach(dev);
 			mod_timer(&link->release, jiffies + HZ / 20);
 		}
 		break;
 	case CS_EVENT_CARD_INSERTION:
 		link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-		dldwd_cs_config(link);
+		orinoco_cs_config(link);
 		break;
 	case CS_EVENT_PM_SUSPEND:
 
 		link->state |= DEV_SUSPEND;
 		/* Fall through... */
 	case CS_EVENT_RESET_PHYSICAL:
-		dldwd_shutdown(priv);
+		orinoco_shutdown(priv);
 		/* Mark the device as stopped, to block IO until later */
 
 		if (link->state & DEV_CONFIG) {
@@ -757,13 +754,13 @@
 				     &link->conf);
 
 			if (link->open) {
-				if (dldwd_reset(priv) == 0) {
+				if (orinoco_reset(priv) == 0) {
 					netif_device_attach(dev);
 					netif_start_queue(dev);
 				} else {
 					printk(KERN_ERR "%s: Error resetting device on PCMCIA event\n",
 					       dev->name);
-					dldwd_cs_stop(dev);
+					orinoco_cs_stop(dev);
 				}
 			}
 		}
@@ -774,17 +771,17 @@
 		break;
 	}
 
-	TRACE_EXIT("dldwd");
+	TRACE_EXIT("orinoco");
 
 	return 0;
-}				/* dldwd_cs_event */
+}				/* orinoco_cs_event */
 
 static int __init
-init_dldwd_cs(void)
+init_orinoco_cs(void)
 {
 	servinfo_t serv;
 
-	TRACE_ENTER("dldwd");
+	TRACE_ENTER("orinoco");
 
 	printk(KERN_DEBUG "%s\n", version);
 
@@ -795,17 +792,17 @@
 		return -1;
 	}
 
-	register_pccard_driver(&dev_info, &dldwd_cs_attach, &dldwd_cs_detach);
+	register_pccard_driver(&dev_info, &orinoco_cs_attach, &orinoco_cs_detach);
 
 
-	TRACE_EXIT("dldwd");
+	TRACE_EXIT("orinoco");
 	return 0;
 }
 
 static void __exit
-exit_dldwd_cs(void)
+exit_orinoco_cs(void)
 {
-	TRACE_ENTER("dldwd");
+	TRACE_ENTER("orinoco");
 
 	unregister_pccard_driver(&dev_info);
 
@@ -814,12 +811,12 @@
 	while (dev_list != NULL) {
 		del_timer(&dev_list->release);
 		if (dev_list->state & DEV_CONFIG)
-			dldwd_cs_release((u_long) dev_list);
-		dldwd_cs_detach(dev_list);
+			orinoco_cs_release((u_long) dev_list);
+		orinoco_cs_detach(dev_list);
 	}
 
-	TRACE_EXIT("dldwd");
+	TRACE_EXIT("orinoco");
 }
 
-module_init(init_dldwd_cs);
-module_exit(exit_dldwd_cs);
+module_init(init_orinoco_cs);
+module_exit(exit_orinoco_cs);
diff -urN linux-2.4.18-pre2/drivers/net/wireless/orinoco_plx.c linux/drivers/net/wireless/orinoco_plx.c
--- linux-2.4.18-pre2/drivers/net/wireless/orinoco_plx.c	Fri Dec 21 09:41:55 2001
+++ linux/drivers/net/wireless/orinoco_plx.c	Thu Jan 10 13:28:35 2002
@@ -1,16 +1,8 @@
-/* orinoco_plx.c 0.01
+/* orinoco_plx.c 0.09
  * 
  * Driver for Prism II devices which would usually be driven by orinoco_cs,
  * but are connected to the PCI bus by a PLX9052. 
  *
- * Specifically here we're talking about the SMC2602W (EZConnect
- * Wireless PCI Adaptor)
- *
- * The actual driving is done by orinoco.c, this is just resource
- * allocation stuff.  The explanation below is courtesy of Ryan Niemi
- * on the linux-wlan-ng list at
- * http://archives.neohapsis.com/archives/dev/linux-wlan/2001-q1/0026.html
- *
  * Copyright (C) 2001 Daniel Barlow <dan@telent.net>
  *
  * The contents of this file are subject to the Mozilla Public License
@@ -34,6 +26,22 @@
  * provisions above, a recipient may use your version of this file
  * under either the MPL or the GPL.
 
+ * Caution: this is experimental and probably buggy.  For success and
+ * failure reports for different cards and adaptors, see
+ * orinoco_plx_pci_id_table near the end of the file.  If you have a
+ * card we don't have the PCI id for, and looks like it should work,
+ * drop me mail with the id and "it works"/"it doesn't work".
+ *
+ * Note: if everything gets detected fine but it doesn't actually send
+ * or receive packets, your first port of call should probably be to   
+ * try newer firmware in the card.  Especially if you're doing Ad-Hoc
+ * modes
+ *
+ * The actual driving is done by orinoco.c, this is just resource
+ * allocation stuff.  The explanation below is courtesy of Ryan Niemi
+ * on the linux-wlan-ng list at
+ * http://archives.neohapsis.com/archives/dev/linux-wlan/2001-q1/0026.html
+
 The PLX9052-based cards (WL11000 and several others) are a different
 beast than the usual PCMCIA-based PRISM2 configuration expected by
 wlan-ng. Here's the general details on how the WL11000 PCI adapter
@@ -95,14 +103,6 @@
 not have time for a while..
 
 ---end of mail---
-
-  Bus  0, device   4, function  0:
-    Network controller: Unknown vendor Unknown device (rev 2).
-      Vendor id=1638. Device id=1100.
-      Medium devsel.  Fast back-to-back capable.  IRQ 10.  
-      I/O at 0x1000 [0x1001].
-      Non-prefetchable 32 bit memory at 0x40000000 [0x40000000].
-      I/O at 0x10c0 [0x10c1].
 */
 
 #include <linux/config.h>
@@ -140,25 +140,31 @@
 #include "hermes.h"
 #include "orinoco.h"
 
+static char version[] __initdata = "orinoco_plx.c 0.09 (Daniel Barlow <dan@telent.net>)";
 MODULE_AUTHOR("Daniel Barlow <dan@telent.net>");
 MODULE_DESCRIPTION("Driver for wireless LAN cards using the PLX9052 PCI bridge");
 MODULE_LICENSE("Dual MPL/GPL");
 
+
+
 static dev_info_t dev_info = "orinoco_plx";
 
-#define COR_OFFSET    0x3e0	/* COR attribute offset of Prism2 PC card */
-#define COR_VALUE     0x41	/* Enable PC card with interrupt in level trigger */
+#define COR_OFFSET    (0x3e0 / 2)	/* COR attribute offset of Prism2 PC card */
+#define COR_VALUE     (COR_LEVEL_REQ | COR_FUNC_ENA) /* Enable PC card with interrupt in level trigger */
+
+#define PLX_INTCSR       0x4c /* Interrupt Control and Status Register */
+#define PLX_INTCSR_INTEN (1<<6) /* Interrupt Enable bit */
 
 static int orinoco_plx_open(struct net_device *dev)
 {
-	dldwd_priv_t *priv = (dldwd_priv_t *) dev->priv;
+	struct orinoco_private *priv = (struct orinoco_private *) dev->priv;
 	int err;
 
 	netif_device_attach(dev);
 
-	err = dldwd_reset(priv);
+	err = orinoco_reset(priv);
 	if (err)
-		printk(KERN_ERR "%s: dldwd_reset failed in orinoco_plx_open()",
+		printk(KERN_ERR "%s: orinoco_reset failed in orinoco_plx_open()",
 		       dev->name);
 	else
 		netif_start_queue(dev);
@@ -168,108 +174,217 @@
 
 static int orinoco_plx_stop(struct net_device *dev)
 {
-	dldwd_priv_t *priv = (dldwd_priv_t *) dev->priv;
+	struct orinoco_private *priv = (struct orinoco_private *) dev->priv;
 	netif_stop_queue(dev);
-	dldwd_shutdown(priv);
+	orinoco_shutdown(priv);
 	return 0;
 }
 
 static void
 orinoco_plx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
-	dldwd_interrupt(irq, ((struct net_device *) dev_id)->priv, regs);
+	orinoco_interrupt(irq, (struct orinoco_private *)dev_id, regs);
 }
 
+static const u16 cis_magic[] = {
+	0x0001, 0x0003, 0x0000, 0x0000, 0x00ff, 0x0017, 0x0004, 0x0067
+};
+
 static int orinoco_plx_init_one(struct pci_dev *pdev,
 				const struct pci_device_id *ent)
 {
-	struct net_device *dev;
-	unsigned long pccard_ioaddr;
+	int err = 0;
+	u16 *attr_mem = NULL;
+	u32 reg, addr;
+	struct orinoco_private *priv = NULL;
+	unsigned long pccard_ioaddr = 0;
+	unsigned long pccard_iolen = 0;
+	struct net_device *dev = NULL;
+	int netdev_registered = 0;
 	int i;
-	int reg;
-	unsigned char *attr_mem;
-	dldwd_priv_t *priv;
 
-	if ((i = pci_enable_device(pdev)))
+	TRACE_ENTER("orinoco_plx");
+
+	err = pci_enable_device(pdev);
+	if (err)
 		return -EIO;
 
 	/* Resource 2 is mapped to the PCMCIA space */
-	attr_mem = ioremap(pci_resource_start(pdev, 2), 0x1000);
-	/* and 3 to the PCMCIA slot I/O address space */
-	pccard_ioaddr = pci_resource_start(pdev, 3);
+	attr_mem = ioremap(pci_resource_start(pdev, 2), PAGE_SIZE);
+	if (! attr_mem)
+		goto fail;
+
+	printk(KERN_DEBUG "orinoco_plx: CIS: ");
+	for (i = 0; i < 16; i++) {
+		printk("%02X:", (int)attr_mem[i]);
+	}
+	printk("\n");
 
 	/* Verify whether PC card is present */
-	if (attr_mem[0] != 0x01 || attr_mem[2] != 0x03 ||
-	    attr_mem[4] != 0x00 || attr_mem[6] != 0x00 ||
-	    attr_mem[8] != 0xFF || attr_mem[10] != 0x17 ||
-	    attr_mem[12] != 0x04 || attr_mem[14] != 0x67) {
+	/* FIXME: we probably need to be smarted about this */
+	if (memcmp(attr_mem, cis_magic, sizeof(cis_magic)) != 0) {
 		printk(KERN_ERR "orinoco_plx: The CIS value of Prism2 PC card is invalid.\n");
-		return -EIO;
+		err = -EIO;
+		goto fail;
 	}
+
 	/* PCMCIA COR is the first byte following CIS: this write should
 	 * enable I/O mode and select level-triggered interrupts */
 	attr_mem[COR_OFFSET] = COR_VALUE;
+	mdelay(1);
 	reg = attr_mem[COR_OFFSET];
-	/* assert(reg==COR_VALUE); doesn't work */
-	iounmap(attr_mem);	/* done with this now, it seems */
-	if (!request_region(pccard_ioaddr,
-			    pci_resource_len(pdev, 3), dev_info)) {
+	if (reg != COR_VALUE) {
+		printk(KERN_ERR "orinoco_plx: Error setting COR value (reg=%x)\n", reg);
+		goto fail;
+	}			
+
+	iounmap(attr_mem);
+	attr_mem = NULL; /* done with this now, it seems */
+
+	/* bjoern: We need to tell the card to enable interrupts, in
+	   case the serial eprom didn't do this already. See the
+	   PLX9052 data book, p8-1 and 8-24 for reference. */
+	addr = pci_resource_start(pdev, 1);
+	reg = 0;
+	reg = inl(addr+PLX_INTCSR);
+	if(reg & PLX_INTCSR_INTEN)
+		printk(KERN_DEBUG "orinoco_plx: "
+		       "Local Interrupt already enabled\n");
+	else {
+		reg |= PLX_INTCSR_INTEN;
+		outl(reg, addr+PLX_INTCSR);
+		reg = inl(addr+PLX_INTCSR);
+		if(!(reg & PLX_INTCSR_INTEN)) {
+			printk(KERN_ERR "orinoco_plx: "
+			       "Couldn't enable Local Interrupts\n");
+			goto fail;
+		}
+	}
+
+	/* and 3 to the PCMCIA slot I/O address space */
+	pccard_ioaddr = pci_resource_start(pdev, 3);
+	pccard_iolen = pci_resource_len(pdev, 3);
+	if (! request_region(pccard_ioaddr, pccard_iolen, dev_info)) {
 		printk(KERN_ERR "orinoco_plx: I/O resource 0x%lx @ 0x%lx busy\n",
-		       pci_resource_len(pdev, 3), pccard_ioaddr);
-		return -EBUSY;
+		       pccard_iolen, pccard_ioaddr);
+		pccard_ioaddr = 0;
+		err = -EBUSY;
+		goto fail;
+	}
+
+	priv = kmalloc(sizeof(*priv), GFP_KERNEL);
+	if (! priv) {
+		err = -ENOMEM;
+		goto fail;
 	}
-	if (!(priv = kmalloc(sizeof(*priv), GFP_KERNEL)))
-		return -ENOMEM;
 	memset(priv, 0, sizeof(*priv));
+
 	dev = &priv->ndev;
 
-	dldwd_setup(priv);	/* XXX clean up if <0 */
-	dev->irq = pdev->irq;
+	err = orinoco_setup(priv);
+	if (err)
+		goto fail;
 	dev->base_addr = pccard_ioaddr;
 	dev->open = orinoco_plx_open;
 	dev->stop = orinoco_plx_stop;
 	priv->card_reset_handler = NULL; /* We have no reset handler */
+	SET_MODULE_OWNER(dev);
 
 	printk(KERN_DEBUG
-	       "Detected Orinoco/Prism2 PCI device at %s, mem:0x%lx, irq:%d, io addr:0x%lx\n",
-	       pdev->slot_name, (long) attr_mem, pdev->irq, pccard_ioaddr);
+	       "Detected Orinoco/Prism2 PLX device at %s irq:%d, io addr:0x%lx\n",
+	       pdev->slot_name, pdev->irq, pccard_ioaddr);
 
-	hermes_struct_init(&(priv->hw), dev->base_addr);	/* XXX */
-	dev->name[0] = '\0';	/* name defaults to ethX */
-	register_netdev(dev);
-	request_irq(pdev->irq, orinoco_plx_interrupt, SA_SHIRQ, dev->name,
-		    dev);
-	if (dldwd_proc_dev_init(priv) != 0) {
-		printk(KERN_ERR "%s: Failed to create /proc node\n", dev->name);
-		return -EIO;
+	hermes_struct_init(&(priv->hw), dev->base_addr);
+	pci_set_drvdata(pdev, priv);
+
+	err = request_irq(pdev->irq, orinoco_plx_interrupt, SA_SHIRQ, dev->name, priv);
+	if (err) {
+		printk(KERN_ERR "orinoco_plx: Error allocating IRQ %d.\n", pdev->irq);
+		err = -EBUSY;
+		goto fail;
 	}
+	dev->irq = pdev->irq;
 
-	SET_MODULE_OWNER(dev);
-	priv->hw_ready = 1;
+	err = register_netdev(dev);
+	if (err)
+		goto fail;
+	netdev_registered = 1;
+
+	err = orinoco_proc_dev_init(priv);
+	if (err)
+		goto fail;
+
+	TRACE_EXIT("orinoco_plx");
 
-	/* if(reset_cor) dldwd_cs_cor_reset(priv); */
 	return 0;		/* succeeded */
+
+ fail:	
+	printk(KERN_DEBUG "orinoco_plx: init_one(), FAIL!\n");
+
+	if (priv) {
+		orinoco_proc_dev_cleanup(priv);
+
+		if (netdev_registered)
+			unregister_netdev(dev);
+		
+		if (dev->irq)
+			free_irq(dev->irq, priv);
+		
+		kfree(priv);
+	}
+
+	if (pccard_ioaddr)
+		release_region(pccard_ioaddr, pccard_iolen);
+
+	if (attr_mem)
+		iounmap(attr_mem);
+
+	pci_disable_device(pdev);
+
+	TRACE_EXIT("orinoco_plx");
+	
+	return err;
 }
 
 static void __devexit orinoco_plx_remove_one(struct pci_dev *pdev)
 {
-	struct net_device *dev = pci_get_drvdata(pdev);
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = pci_get_drvdata(pdev);
+	struct net_device *dev = &priv->ndev;
 
-	if (!dev)
+	TRACE_ENTER("orinoco_plx");
+
+	if (!priv)
 		BUG();
 
-	dldwd_proc_dev_cleanup(priv);
-	free_irq(dev->irq, dev);
+	orinoco_proc_dev_cleanup(priv);
+
 	unregister_netdev(dev);
-	release_region(dev->base_addr, 0x40);
-	kfree(dev->priv);
-	pci_set_drvdata(pdev, NULL);
+		
+	if (dev->irq)
+		free_irq(dev->irq, priv);
+		
+	kfree(priv);
+
+	release_region(pci_resource_start(pdev, 3), pci_resource_len(pdev, 3));
+
+	pci_disable_device(pdev);
+
+	TRACE_EXIT("orinoco_plx");
 }
 
 
 static struct pci_device_id orinoco_plx_pci_id_table[] __devinitdata = {
-	{0x1638, 0x1100, PCI_ANY_ID, PCI_ANY_ID,},
+	{0x1385, 0x4100, PCI_ANY_ID, PCI_ANY_ID,},	/* Netgear MA301 */
+#if 0
+	{0x15e8, 0x0130, PCI_ANY_ID, PCI_ANY_ID,},	/* Correga */
+#endif
+	{0x1638, 0x1100, PCI_ANY_ID, PCI_ANY_ID,},	/* SMC EZConnect SMC2602W,
+							   Eumitcom PCI WL11000,
+							   Addtron AWA-100*/
+	{0x16ab, 0x1100, PCI_ANY_ID, PCI_ANY_ID,},	/* Global Sun Tech GL24110P */
+	{0x16ab, 0x1101, PCI_ANY_ID, PCI_ANY_ID,},	/* Reported working, but unknown */
+	{0x16ab, 0x1102, PCI_ANY_ID, PCI_ANY_ID,},	/* Linksys WDT11 */
+	{0x16ec, 0x3685, PCI_ANY_ID, PCI_ANY_ID,},	/* USR 2415 */
 	{0,},
 };
 
@@ -279,19 +394,22 @@
 	name:"orinoco_plx",
 	id_table:orinoco_plx_pci_id_table,
 	probe:orinoco_plx_init_one,
-	remove:__devexit_p(orinoco_plx_remove_one),
+	remove:orinoco_plx_remove_one,
 	suspend:0,
 	resume:0
 };
 
 static int __init orinoco_plx_init(void)
 {
+	printk(KERN_DEBUG "%s\n", version);
 	return pci_module_init(&orinoco_plx_driver);
 }
 
 extern void __exit orinoco_plx_exit(void)
 {
 	pci_unregister_driver(&orinoco_plx_driver);
+	current->state = TASK_UNINTERRUPTIBLE;
+	schedule_timeout(HZ);
 }
 
 module_init(orinoco_plx_init);
diff -urN linux-2.4.18-pre2/drivers/pci/pci.ids linux/drivers/pci/pci.ids
--- linux-2.4.18-pre2/drivers/pci/pci.ids	Fri Nov  9 14:03:11 2001
+++ linux/drivers/pci/pci.ids	Thu Jan 10 13:28:35 2002
@@ -1179,11 +1179,21 @@
 	0017  Paddington Mac I/O
 	0018  UniNorth FireWire
 	0019  KeyLargo USB
-	001e  UniNorth PCI
+	001e  UniNorth Internal PCI
 	001f  UniNorth PCI
 	0020  UniNorth AGP
-	0021  UniNorth GMAC
+	0021  UniNorth GMAC (Sun GEM)
 	0022  KeyLargo Mac I/O
+	0024  UniNorth/Pangea GMAC (Sun GEM)
+	0025  KeyLargo/Pangea Mac I/O
+	0026  KeyLargo/Pangea USB
+	0027  UniNorth/Pangea AGP
+	0028  UniNorth/Pangea PCI
+	0029  UniNorth/Pangea Internal PCI
+	002d  UniNorth 1.5 AGP
+	002e  UniNorth 1.5 PCI
+	002f  UniNorth 1.5 Internal PCI
+	0030  UniNorth/Pangea FireWire
 106c  Hyundai Electronics America
 	8801  Dual Pentium ISA/PCI Motherboard
 	8802  PowerPC ISA/PCI Motherboard
@@ -2601,6 +2611,8 @@
 	0005  ATP850UF
 	0006  ATP860 NO-BIOS
 	0007  ATP860
+	0008  ATP865 NO-ROM
+	0009  ATP865
 	8002  AEC6710 SCSI-2 Host Adapter
 	8010  AEC6712UW SCSI
 	8020  AEC6712U SCSI
diff -urN linux-2.4.18-pre2/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- linux-2.4.18-pre2/drivers/scsi/Config.in	Fri Dec 21 09:41:55 2001
+++ linux/drivers/scsi/Config.in	Thu Jan 10 13:28:35 2002
@@ -214,7 +214,7 @@
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
    dep_tristate 'SCSI debugging host simulator (EXPERIMENTAL)' CONFIG_SCSI_DEBUG $CONFIG_SCSI
 fi
-if [ "$CONFIG_PPC" = "y" ]; then
+if [ "$CONFIG_ALL_PPC" = "y" ]; then
    dep_tristate 'MESH (Power Mac internal SCSI) support' CONFIG_SCSI_MESH $CONFIG_SCSI
    if [ "$CONFIG_SCSI_MESH" != "n" ]; then
       int '  maximum synchronous transfer rate (MB/s) (0 = async)' CONFIG_SCSI_MESH_SYNC_RATE 5
diff -urN linux-2.4.18-pre2/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c
--- linux-2.4.18-pre2/drivers/scsi/hosts.c	Thu Jul  5 11:28:17 2001
+++ linux/drivers/scsi/hosts.c	Thu Jan 10 13:28:35 2002
@@ -275,6 +275,24 @@
     return 0;
 }
 
+void
+scsi_deregister_device(struct Scsi_Device_Template * tpnt)
+{
+    struct Scsi_Device_Template *spnt;
+    struct Scsi_Device_Template *prev_spnt;
+
+    spnt = scsi_devicelist;
+    prev_spnt = NULL;
+    while (spnt != tpnt) {
+	prev_spnt = spnt;
+	spnt = spnt->next;
+    }
+    if (prev_spnt == NULL)
+        scsi_devicelist = tpnt->next;
+    else
+        prev_spnt->next = spnt->next;
+}
+
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
  * Emacs will notice this stuff at the end of the file and automatically
diff -urN linux-2.4.18-pre2/drivers/scsi/hosts.h linux/drivers/scsi/hosts.h
--- linux-2.4.18-pre2/drivers/scsi/hosts.h	Thu Nov 22 11:49:15 2001
+++ linux/drivers/scsi/hosts.h	Thu Jan 10 13:28:35 2002
@@ -526,6 +526,7 @@
 void  scsi_initialize_queue(Scsi_Device * SDpnt, struct Scsi_Host * SHpnt);
 
 int scsi_register_device(struct Scsi_Device_Template * sdpnt);
+void scsi_deregister_device(struct Scsi_Device_Template * tpnt);
 
 /* These are used by loadable modules */
 extern int scsi_register_module(int, void *);
diff -urN linux-2.4.18-pre2/drivers/scsi/mesh.c linux/drivers/scsi/mesh.c
--- linux-2.4.18-pre2/drivers/scsi/mesh.c	Tue Sep 18 14:23:14 2001
+++ linux/drivers/scsi/mesh.c	Thu Jan 10 13:28:35 2002
@@ -28,7 +28,8 @@
 #include <asm/irq.h>
 #include <asm/hydra.h>
 #include <asm/processor.h>
-#include <asm/feature.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
 #ifdef CONFIG_PMAC_PBOOK
 #include <linux/adb.h>
 #include <linux/pmu.h>
@@ -155,7 +156,6 @@
 	struct mesh_target tgts[8];
 	void	*dma_cmd_space;
 	struct device_node *ofnode;
-	u8*	mio_base;
 #ifndef MESH_NEW_STYLE_EH
 	Scsi_Cmnd *completed_q;
 	Scsi_Cmnd *completed_qtail;
@@ -258,8 +258,6 @@
 	if (mesh == 0)
 		mesh = find_compatible_devices("scsi", "chrp,mesh0");
 	for (; mesh != 0; mesh = mesh->next) {
-		struct device_node *mio;
-		
 		if (mesh->n_addrs != 2 || mesh->n_intrs != 2) {
 			printk(KERN_ERR "mesh: expected 2 addrs and 2 intrs"
 			       " (got %d,%d)", mesh->n_addrs, mesh->n_intrs);
@@ -325,12 +323,6 @@
 		if (mesh_sync_period < minper)
 			mesh_sync_period = minper;
 
-		ms->mio_base = 0;
-		for (mio = ms->ofnode->parent; mio; mio = mio->parent)
-			if (strcmp(mio->name, "mac-io") == 0 && mio->n_addrs > 0)
-				break;
-		if (mio)
-			ms->mio_base = (u8 *) ioremap(mio->addrs[0].address, 0x40);
 		set_mesh_power(ms, 1);
 
 		mesh_init(ms);
@@ -363,11 +355,9 @@
 		iounmap((void *) ms->mesh);
 	if (ms->dma)
 		iounmap((void *) ms->dma);
-	if (ms->mio_base)
-		iounmap((void *) ms->mio_base);
 	kfree(ms->dma_cmd_space);
 	free_irq(ms->meshintr, ms);
-	feature_clear(ms->ofnode, FEATURE_MESH_enable);
+	pmac_call_feature(PMAC_FTR_MESH_ENABLE, ms->ofnode, 0, 0);
 	return 0;
 }
 
@@ -377,16 +367,10 @@
 	if (_machine != _MACH_Pmac)
 		return;
 	if (state) {
-		feature_set(ms->ofnode, FEATURE_MESH_enable);
-		/* This seems to enable the termination power. strangely
-		   this doesn't fully agree with OF, but with MacOS */
-		if (ms->mio_base)
-			out_8(ms->mio_base + 0x36, 0x70);
+		pmac_call_feature(PMAC_FTR_MESH_ENABLE, ms->ofnode, 0, 1);
 		mdelay(200);
 	} else {
-		feature_clear(ms->ofnode, FEATURE_MESH_enable);
-		if (ms->mio_base)
-			out_8(ms->mio_base + 0x36, 0x34);
+		pmac_call_feature(PMAC_FTR_MESH_ENABLE, ms->ofnode, 0, 0);
 		mdelay(10);
 	}
 }			
diff -urN linux-2.4.18-pre2/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- linux-2.4.18-pre2/drivers/scsi/scsi.c	Fri Dec 21 09:41:55 2001
+++ linux/drivers/scsi/scsi.c	Thu Jan 10 13:28:35 2002
@@ -2261,7 +2261,7 @@
 		for (SDpnt = shpnt->host_queue; SDpnt;
 		     SDpnt = SDpnt->next) {
 			if (tpnt->detect)
-				SDpnt->attached += (*tpnt->detect) (SDpnt);
+				SDpnt->detected = (*tpnt->detect) (SDpnt);
 		}
 	}
 
@@ -2269,9 +2269,19 @@
 	 * If any of the devices would match this driver, then perform the
 	 * init function.
 	 */
-	if (tpnt->init && tpnt->dev_noticed)
-		if ((*tpnt->init) ())
+	if (tpnt->init && tpnt->dev_noticed) {
+		if ((*tpnt->init) ()) {
+			for (shpnt = scsi_hostlist; shpnt;
+			     shpnt = shpnt->next) {
+				for (SDpnt = shpnt->host_queue; SDpnt;
+				     SDpnt = SDpnt->next) {
+					SDpnt->detected = 0;
+				}
+			}
+			scsi_deregister_device(tpnt);
 			return 1;
+		}
+	}
 
 	/*
 	 * Now actually connect the devices to the new driver.
@@ -2279,6 +2289,8 @@
 	for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
 		for (SDpnt = shpnt->host_queue; SDpnt;
 		     SDpnt = SDpnt->next) {
+			SDpnt->attached += SDpnt->detected;
+			SDpnt->detected = 0;
 			if (tpnt->attach)
 				(*tpnt->attach) (SDpnt);
 			/*
@@ -2314,9 +2326,7 @@
 {
 	Scsi_Device *SDpnt;
 	struct Scsi_Host *shpnt;
-	struct Scsi_Device_Template *spnt;
-	struct Scsi_Device_Template *prev_spnt;
-	
+
 	lock_kernel();
 	/*
 	 * If we are busy, this is not going to fly.
@@ -2347,16 +2357,7 @@
 	/*
 	 * Extract the template from the linked list.
 	 */
-	spnt = scsi_devicelist;
-	prev_spnt = NULL;
-	while (spnt != tpnt) {
-		prev_spnt = spnt;
-		spnt = spnt->next;
-	}
-	if (prev_spnt == NULL)
-		scsi_devicelist = tpnt->next;
-	else
-		prev_spnt->next = spnt->next;
+	scsi_deregister_device(tpnt);
 
 	MOD_DEC_USE_COUNT;
 	unlock_kernel();
diff -urN linux-2.4.18-pre2/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h
--- linux-2.4.18-pre2/drivers/scsi/scsi.h	Thu Nov 22 11:49:15 2001
+++ linux/drivers/scsi/scsi.h	Thu Jan 10 13:28:35 2002
@@ -575,8 +575,8 @@
 					 * vendor-specific cmd's */
 	unsigned sector_size;	/* size in bytes */
 
-	int attached;		/* # of high level drivers attached to 
-				 * this */
+	int attached;		/* # of high level drivers attached to this */
+	int detected;		/* Delta attached - don't use in drivers! */
 	int access_count;	/* Count of open channels/mounts */
 
 	void *hostdata;		/* available to low-level driver */
diff -urN linux-2.4.18-pre2/drivers/scsi/sd.c linux/drivers/scsi/sd.c
--- linux-2.4.18-pre2/drivers/scsi/sd.c	Fri Nov  9 14:05:06 2001
+++ linux/drivers/scsi/sd.c	Thu Jan 10 13:28:35 2002
@@ -1078,6 +1078,7 @@
 		for (i = 0; i < N_USED_SD_MAJORS; i++) {
 			if (devfs_register_blkdev(SD_MAJOR(i), "sd", &sd_fops)) {
 				printk("Unable to get major %d for SCSI disk\n", SD_MAJOR(i));
+				sd_template.dev_noticed = 0;
 				return 1;
 			}
 		}
@@ -1175,7 +1176,8 @@
 		kfree(sd_gendisks[i].de_arr);
 		kfree(sd_gendisks[i].flags);
 	}
-	kfree(sd_gendisks);
+	if (sd_gendisks != &sd_gendisk)
+		kfree(sd_gendisks);
 cleanup_sd_gendisks:
 	kfree(sd);
 cleanup_sd:
@@ -1188,11 +1190,13 @@
 	kfree(sd_sizes);
 cleanup_disks:
 	kfree(rscsi_disks);
+	rscsi_disks = NULL;
 cleanup_devfs:
 	for (i = 0; i < N_USED_SD_MAJORS; i++) {
 		devfs_unregister_blkdev(SD_MAJOR(i), "sd");
 	}
 	sd_registered--;
+	sd_template.dev_noticed = 0;
 	return 1;
 }
 
@@ -1251,7 +1255,7 @@
 	if (SDp->type != TYPE_DISK && SDp->type != TYPE_MOD)
 		return 0;
 
-	if (sd_template.nr_dev >= sd_template.dev_max) {
+	if (sd_template.nr_dev >= sd_template.dev_max || rscsi_disks == NULL) {
 		SDp->attached--;
 		return 1;
 	}
@@ -1259,8 +1263,13 @@
 		if (!dpnt->device)
 			break;
 
-	if (i >= sd_template.dev_max)
-		panic("scsi_devices corrupt (sd)");
+	if (i >= sd_template.dev_max) {
+		printk(KERN_WARNING "scsi_devices corrupt (sd),"
+		    " nr_dev %d dev_max %d\n",
+		    sd_template.nr_dev, sd_template.dev_max);
+		SDp->attached--;
+		return 1;
+	}
 
 	rscsi_disks[i].device = SDp;
 	rscsi_disks[i].has_part_table = 0;
@@ -1347,6 +1356,9 @@
 	int i, j;
 	int max_p;
 	int start;
+
+	if (rscsi_disks == NULL)
+		return;
 
 	for (dpnt = rscsi_disks, i = 0; i < sd_template.dev_max; i++, dpnt++)
 		if (dpnt->device == SDp) {
diff -urN linux-2.4.18-pre2/drivers/scsi/sg.c linux/drivers/scsi/sg.c
--- linux-2.4.18-pre2/drivers/scsi/sg.c	Fri Dec 21 09:41:55 2001
+++ linux/drivers/scsi/sg.c	Thu Jan 10 13:28:35 2002
@@ -1344,6 +1344,7 @@
             printk(KERN_ERR "Unable to get major %d for generic SCSI device\n",
                    SCSI_GENERIC_MAJOR);
 	    write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
+            sg_template.dev_noticed = 0;
             return 1;
         }
         sg_registered++;
@@ -1356,6 +1357,7 @@
     if (NULL == sg_dev_arr) {
         printk(KERN_ERR "sg_init: no space for sg_dev_arr\n");
 	write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
+        sg_template.dev_noticed = 0;
         return 1;
     }
     memset(sg_dev_arr, 0, sg_template.dev_max * sizeof(Sg_device *));
diff -urN linux-2.4.18-pre2/drivers/scsi/sr.c linux/drivers/scsi/sr.c
--- linux-2.4.18-pre2/drivers/scsi/sr.c	Thu Oct 25 13:58:35 2001
+++ linux/drivers/scsi/sr.c	Thu Jan 10 13:28:35 2002
@@ -789,6 +789,7 @@
 	if (!sr_registered) {
 		if (devfs_register_blkdev(MAJOR_NR, "sr", &sr_bdops)) {
 			printk("Unable to get major %d for SCSI-CD\n", MAJOR_NR);
+			sr_template.dev_noticed = 0;
 			return 1;
 		}
 		sr_registered++;
@@ -830,8 +831,10 @@
 	kfree(sr_sizes);
 cleanup_cds:
 	kfree(scsi_CDs);
+	scsi_CDs = NULL;
 cleanup_devfs:
 	devfs_unregister_blkdev(MAJOR_NR, "sr");
+	sr_template.dev_noticed = 0;
 	sr_registered--;
 	return 1;
 }
@@ -905,6 +908,8 @@
 	Scsi_CD *cpnt;
 	int i;
 
+	if (scsi_CDs == NULL)
+		return;
 	for (cpnt = scsi_CDs, i = 0; i < sr_template.dev_max; i++, cpnt++)
 		if (cpnt->device == SDp) {
 			/*
diff -urN linux-2.4.18-pre2/drivers/scsi/st.c linux/drivers/scsi/st.c
--- linux-2.4.18-pre2/drivers/scsi/st.c	Fri Nov  9 13:52:21 2001
+++ linux/drivers/scsi/st.c	Thu Jan 10 13:28:35 2002
@@ -3818,6 +3818,7 @@
 			write_unlock_irqrestore(&st_dev_arr_lock, flags);
 			printk(KERN_ERR "Unable to get major %d for SCSI tapes\n",
                                MAJOR_NR);
+			st_template.dev_noticed = 0;
 			return 1;
 		}
 		st_registered++;
diff -urN linux-2.4.18-pre2/drivers/usb/Config.in linux/drivers/usb/Config.in
--- linux-2.4.18-pre2/drivers/usb/Config.in	Thu Jan 10 13:28:30 2002
+++ linux/drivers/usb/Config.in	Thu Jan 10 13:28:35 2002
@@ -32,6 +32,9 @@
 comment 'USB Device Class drivers'
 dep_tristate '  USB Audio support' CONFIG_USB_AUDIO $CONFIG_USB $CONFIG_SOUND
 dep_tristate '  USB Bluetooth support (EXPERIMENTAL)' CONFIG_USB_BLUETOOTH $CONFIG_USB $CONFIG_EXPERIMENTAL
+if [ "$CONFIG_SCSI" = "n" ]; then
+   comment '  SCSI support is needed for USB Storage'
+fi
 dep_tristate '  USB Mass Storage support' CONFIG_USB_STORAGE $CONFIG_USB $CONFIG_SCSI
    dep_mbool '    USB Mass Storage verbose debug' CONFIG_USB_STORAGE_DEBUG $CONFIG_USB_STORAGE
    dep_mbool '    Datafab MDCFE-B Compact Flash Reader support' CONFIG_USB_STORAGE_DATAFAB $CONFIG_USB_STORAGE $CONFIG_EXPERIMENTAL
diff -urN linux-2.4.18-pre2/drivers/usb/Makefile linux/drivers/usb/Makefile
--- linux-2.4.18-pre2/drivers/usb/Makefile	Thu Jan 10 13:28:30 2002
+++ linux/drivers/usb/Makefile	Thu Jan 10 13:28:35 2002
@@ -10,19 +10,13 @@
 
 # Objects that export symbols.
 
-export-objs		:= usb.o
+export-objs		:= usb.o ov511.o pwc-uncompress.o
 
 # Multipart objects.
 
-list-multi		:= usbcore.o hid.o
+list-multi		:= usbcore.o hid.o pwc.o
 usbcore-objs		:= usb.o usb-debug.o hub.o
 hid-objs		:= hid-core.o hid-input.o
-
-ifneq ($(CONFIG_USB_PWC),n)
-	export-objs		+= pwc-uncompress.o
-	list-multi		+= pwc.o
-endif
-
 pwc-objs		:= pwc-if.o pwc-misc.o pwc-ctrl.o pwc-uncompress.o
 
 
diff -urN linux-2.4.18-pre2/drivers/usb/ov511.c linux/drivers/usb/ov511.c
--- linux-2.4.18-pre2/drivers/usb/ov511.c	Fri Sep 14 14:04:07 2001
+++ linux/drivers/usb/ov511.c	Thu Jan 10 13:28:35 2002
@@ -1,13 +1,16 @@
 /*
  * OmniVision OV511 Camera-to-USB Bridge Driver
  *
- * Copyright (c) 1999-2000 Mark W. McClelland
+ * Copyright (c) 1999-2001 Mark W. McClelland
+ * Original decompression code Copyright 1998-2000 OmniVision Technologies
  * Many improvements by Bret Wallach <bwallac1@san.rr.com>
  * Color fixes by by Orion Sky Lawlor <olawlor@acm.org> (2/26/2000)
  * Snapshot code by Kevin Moore
  * OV7620 fixes by Charl P. Botha <cpbotha@ieee.org>
  * Changes by Claudio Matsuoka <claudio@conectiva.com>
- * 
+ * Original SAA7111A code by Dave Perks <dperks@ibm.net>
+ * Kernel I2C interface adapted from nt1003 driver
+ *
  * Based on the Linux CPiA driver written by Peter Pregler,
  * Scott J. Bertin and Johannes Erdfelt.
  * 
@@ -30,11 +33,9 @@
  * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-#define __NO_VERSION__
-
 #include <linux/config.h>
-#include <linux/module.h>
 #include <linux/version.h>
+#include <linux/module.h>
 #include <linux/init.h>
 #include <linux/fs.h>
 #include <linux/vmalloc.h>
@@ -42,32 +43,51 @@
 #include <linux/proc_fs.h>
 #include <linux/ctype.h>
 #include <linux/pagemap.h>
-#include <linux/usb.h>
 #include <asm/io.h>
 #include <asm/semaphore.h>
+#include <asm/processor.h>
 #include <linux/wrapper.h>
 
+#if defined (__i386__)
+	#include <asm/cpufeature.h>
+#endif
+
 #include "ov511.h"
 
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v1.28"
-#define DRIVER_AUTHOR "Mark McClelland <mwm@i.am> & Bret Wallach & Orion Sky Lawlor <olawlor@acm.org> & Kevin Moore & Charl P. Botha <cpbotha@ieee.org> & Claudio Matsuoka <claudio@conectiva.com>"
+#define DRIVER_VERSION "v1.48 for Linux 2.4"
+#define EMAIL "mmcclell@bigfoot.com"
+#define DRIVER_AUTHOR "Mark McClelland <mmcclell@bigfoot.com> & Bret Wallach \
+	& Orion Sky Lawlor <olawlor@acm.org> & Kevin Moore & Charl P. Botha \
+	<cpbotha@ieee.org> & Claudio Matsuoka <claudio@conectiva.com>"
 #define DRIVER_DESC "OV511 USB Camera Driver"
 
 #define OV511_I2C_RETRIES 3
+#define ENABLE_Y_QUANTABLE 1
+#define ENABLE_UV_QUANTABLE 1
+
+/* Pixel count * 3 bytes for RGB */
+#define MAX_FRAME_SIZE(w, h) ((w) * (h) * 3)
+#define MAX_DATA_SIZE(w, h) (MAX_FRAME_SIZE(w, h) + sizeof(struct timeval))
 
-/* Video Size 640 x 480 x 3 bytes for RGB */
-#define MAX_FRAME_SIZE (640 * 480 * 3)
-#define MAX_DATA_SIZE (MAX_FRAME_SIZE + sizeof(struct timeval))
+/* Max size * bytes per YUV420 pixel (1.5) + one extra isoc frame for safety */
+#define MAX_RAW_DATA_SIZE(w, h) ((w) * (h) * 3 / 2 + 1024)
 
-#define GET_SEGSIZE(p) ((p) == VIDEO_PALETTE_GREY ? 256 : 384)
+#define FATAL_ERROR(rc) ((rc) < 0 && (rc) != -EPERM)
 
 /* PARAMETER VARIABLES: */
-static int autoadjust = 1;    /* CCD dynamically changes exposure, etc... */
+/* (See ov511.txt for detailed descriptions of these.) */
 
-static int video_nr = -1;
+/* Sensor automatically changes brightness */
+static int autobright = 1;
+
+/* Sensor automatically changes gain */
+static int autogain = 1;
+
+/* Sensor automatically changes exposure */
+static int autoexp = 1;
 
 /* 0=no debug messages
  * 1=init/detection/unload and other significant messages,
@@ -77,27 +97,17 @@
  * 5=highly repetitive mesgs
  * NOTE: This should be changed to 0, 1, or 2 for production kernels
  */
-static int debug = 0;
+static int debug; /* = 0 */
 
 /* Fix vertical misalignment of red and blue at 640x480 */
-static int fix_rgb_offset = 0;
+static int fix_rgb_offset; /* = 0 */
 
 /* Snapshot mode enabled flag */
-static int snapshot = 0;
-
-/* Sensor detection override (global for all attached cameras) */
-static int sensor = 0;
-
-/* Increase this if you are getting "Failed to read sensor ID..." */
-static int i2c_detect_tries = 5;
-
-/* For legal values, see the OV7610/7620 specs under register Common F,
- * upper nybble  (set to 0-F) */
-static int aperture = -1;
+static int snapshot; /* = 0 */
 
 /* Force image to be read in RGB instead of BGR. This option allow
  * programs that expect RGB data (e.g. gqcam) to work with this driver. */
-static int force_rgb = 0;
+static int force_rgb; /* = 0 */
 
 /* Number of seconds before inactive buffers are deallocated */
 static int buf_timeout = 5;
@@ -105,76 +115,219 @@
 /* Number of cameras to stream from simultaneously */
 static int cams = 1;
 
-/* Prevent apps from timing out if frame is not done in time */
-static int retry_sync = 0;
-
-/* Enable compression. This is for experimentation only; compressed images
- * still cannot be decoded yet. */
-static int compress = 0;
+/* Enable compression. Needs a fast (>300 MHz) CPU. */
+static int compress; /* = 0 */
 
 /* Display test pattern - doesn't work yet either */
-static int testpat = 0;
+static int testpat; /* = 0 */
 
-/* Setting this to 1 will make the sensor output GBR422 instead on YUV420. Only
+/* Setting this to 1 will make the sensor output GBR422 instead of YUV420. Only
  * affects RGB24 mode. */
-static int sensor_gbr = 0;
+static int sensor_gbr; /* = 0 */
 
-/* Dump raw pixel data, in one of 3 formats. See ov511_dumppix() for details. */
-static int dumppix = 0;
+/* Dump raw pixel data. */
+static int dumppix; /* = 0 */
 
-MODULE_PARM(autoadjust, "i");
-MODULE_PARM_DESC(autoadjust, "CCD dynamically changes exposure");
+/* LED policy. Only works on some OV511+ cameras. 0=off, 1=on (default), 2=auto
+ * (on when open) */
+static int led = 1;
+
+/* Set this to 1 to dump the bridge register contents after initialization */
+static int dump_bridge; /* = 0 */
+
+/* Set this to 1 to dump the sensor register contents after initialization */
+static int dump_sensor; /* = 0 */
+
+/* Temporary option for debugging "works, but no image" problem. Prints the
+ * first 12 bytes of data (potentially a packet header) in each isochronous
+ * data frame. */
+static int printph; /* = 0 */
+
+/* Compression parameters - I'm not exactly sure what these do yet */
+static int phy = 0x1f;
+static int phuv = 0x05;
+static int pvy = 0x06;
+static int pvuv = 0x06;
+static int qhy = 0x14;
+static int qhuv = 0x03;
+static int qvy = 0x04;
+static int qvuv = 0x04;
+
+/* Light frequency. Set to 50 or 60 (Hz), or zero for default settings */
+static int lightfreq; /* = 0 */
+
+/* Set this to 1 to enable banding filter by default. Compensates for
+ * alternating horizontal light/dark bands caused by (usually fluorescent)
+ * lights */
+static int bandingfilter; /* = 0 */
+
+/* Pixel clock divisor */
+static int clockdiv = -1;
+
+/* Isoc packet size */
+static int packetsize = -1;
+
+/* Frame drop register (16h) */
+static int framedrop = -1;
+
+/* Allows picture settings (brightness, hue, etc...) to take effect immediately,
+ * even in the middle of a frame. This reduces the time to change settings, but
+ * can ruin frames during the change. Only affects OmniVision sensors. */
+static int fastset; /* = 0 */
+
+/* Forces the palette to a specific value. If an application requests a
+ * different palette, it will be rejected. */
+static int force_palette; /* = 0 */
+
+/* Set tuner type, if not autodetected */
+static int tuner = -1;
+
+/* Allows proper exposure of objects that are illuminated from behind. Only
+ * affects OmniVision sensors. */
+static int backlight; /* = 0 */
+
+/* If you change this, you must also change the MODULE_PARM definition */
+#define OV511_MAX_UNIT_VIDEO 16
+
+/* Allows specified minor numbers to be forced. They will be assigned in the
+ * order that devices are detected. Note that you cannot specify 0 as a minor
+ * number. If you do not specify any, the next available one will be used. This
+ * requires kernel 2.4.5 or later. */
+static int unit_video[OV511_MAX_UNIT_VIDEO];
+
+/* Remove zero-padding from uncompressed incoming data. This will compensate for
+ * the blocks of corruption that appear when the camera cannot keep up with the
+ * speed of the USB bus (eg. at low frame resolutions) */
+static int remove_zeros; /* = 0 */
+
+MODULE_PARM(autobright, "i");
+MODULE_PARM_DESC(autobright, "Sensor automatically changes brightness");
+MODULE_PARM(autogain, "i");
+MODULE_PARM_DESC(autogain, "Sensor automatically changes gain");
+MODULE_PARM(autoexp, "i");
+MODULE_PARM_DESC(autoexp, "Sensor automatically changes exposure");
 MODULE_PARM(debug, "i");
-MODULE_PARM_DESC(debug, "Debug level: 0=none, 1=init/detection, 2=warning, 3=config/control, 4=function call, 5=max");
+MODULE_PARM_DESC(debug,
+  "Debug level: 0=none, 1=inits, 2=warning, 3=config, 4=functions, 5=max");
 MODULE_PARM(fix_rgb_offset, "i");
-MODULE_PARM_DESC(fix_rgb_offset, "Fix vertical misalignment of red and blue at 640x480");
+MODULE_PARM_DESC(fix_rgb_offset,
+  "Fix vertical misalignment of red and blue at 640x480");
 MODULE_PARM(snapshot, "i");
 MODULE_PARM_DESC(snapshot, "Enable snapshot mode");
-MODULE_PARM(sensor, "i");
-MODULE_PARM_DESC(sensor, "Override sensor detection");
-MODULE_PARM(i2c_detect_tries, "i");
-MODULE_PARM_DESC(i2c_detect_tries, "Number of tries to detect sensor");
-MODULE_PARM(aperture, "i");
-MODULE_PARM_DESC(aperture, "Read the OV7610/7620 specs");
 MODULE_PARM(force_rgb, "i");
 MODULE_PARM_DESC(force_rgb, "Read RGB instead of BGR");
 MODULE_PARM(buf_timeout, "i");
 MODULE_PARM_DESC(buf_timeout, "Number of seconds before buffer deallocation");
 MODULE_PARM(cams, "i");
 MODULE_PARM_DESC(cams, "Number of simultaneous cameras");
-MODULE_PARM(retry_sync, "i");
-MODULE_PARM_DESC(retry_sync, "Prevent apps from timing out");
 MODULE_PARM(compress, "i");
-MODULE_PARM_DESC(compress, "Turn on compression (not functional yet)");
+MODULE_PARM_DESC(compress, "Turn on compression (not reliable yet)");
 MODULE_PARM(testpat, "i");
-MODULE_PARM_DESC(testpat, "Replace image with vertical bar testpattern (only partially working)");
-MODULE_PARM(sensor_gbr, "i");
-MODULE_PARM_DESC(sensor_gbr, "Make sensor output GBR422 rather than YUV420");
+MODULE_PARM_DESC(testpat,
+  "Replace image with vertical bar testpattern (only partially working)");
+
+// Temporarily removed (needs to be rewritten for new format conversion code)
+// MODULE_PARM(sensor_gbr, "i");
+// MODULE_PARM_DESC(sensor_gbr, "Make sensor output GBR422 rather than YUV420");
+
 MODULE_PARM(dumppix, "i");
-MODULE_PARM_DESC(dumppix, "Dump raw pixel data, in one of 3 formats. See ov511_dumppix() for details");
-MODULE_PARM(video_nr,"i");
+MODULE_PARM_DESC(dumppix, "Dump raw pixel data");
+MODULE_PARM(led, "i");
+MODULE_PARM_DESC(led,
+  "LED policy (OV511+ or later). 0=off, 1=on (default), 2=auto (on when open)");
+MODULE_PARM(dump_bridge, "i");
+MODULE_PARM_DESC(dump_bridge, "Dump the bridge registers");
+MODULE_PARM(dump_sensor, "i");
+MODULE_PARM_DESC(dump_sensor, "Dump the sensor registers");
+MODULE_PARM(printph, "i");
+MODULE_PARM_DESC(printph, "Print frame start/end headers");
+MODULE_PARM(phy, "i");
+MODULE_PARM_DESC(phy, "Prediction range (horiz. Y)");
+MODULE_PARM(phuv, "i");
+MODULE_PARM_DESC(phuv, "Prediction range (horiz. UV)");
+MODULE_PARM(pvy, "i");
+MODULE_PARM_DESC(pvy, "Prediction range (vert. Y)");
+MODULE_PARM(pvuv, "i");
+MODULE_PARM_DESC(pvuv, "Prediction range (vert. UV)");
+MODULE_PARM(qhy, "i");
+MODULE_PARM_DESC(qhy, "Quantization threshold (horiz. Y)");
+MODULE_PARM(qhuv, "i");
+MODULE_PARM_DESC(qhuv, "Quantization threshold (horiz. UV)");
+MODULE_PARM(qvy, "i");
+MODULE_PARM_DESC(qvy, "Quantization threshold (vert. Y)");
+MODULE_PARM(qvuv, "i");
+MODULE_PARM_DESC(qvuv, "Quantization threshold (vert. UV)");
+MODULE_PARM(lightfreq, "i");
+MODULE_PARM_DESC(lightfreq,
+  "Light frequency. Set to 50 or 60 Hz, or zero for default settings");
+MODULE_PARM(bandingfilter, "i");
+MODULE_PARM_DESC(bandingfilter,
+  "Enable banding filter (to reduce effects of fluorescent lighting)");
+MODULE_PARM(clockdiv, "i");
+MODULE_PARM_DESC(clockdiv, "Force pixel clock divisor to a specific value");
+MODULE_PARM(packetsize, "i");
+MODULE_PARM_DESC(packetsize, "Force a specific isoc packet size");
+MODULE_PARM(framedrop, "i");
+MODULE_PARM_DESC(framedrop, "Force a specific frame drop register setting");
+MODULE_PARM(fastset, "i");
+MODULE_PARM_DESC(fastset, "Allows picture settings to take effect immediately");
+MODULE_PARM(force_palette, "i");
+MODULE_PARM_DESC(force_palette, "Force the palette to a specific value");
+MODULE_PARM(tuner, "i");
+MODULE_PARM_DESC(tuner, "Set tuner type, if not autodetected");
+MODULE_PARM(backlight, "i");
+MODULE_PARM_DESC(backlight, "For objects that are lit from behind");
+MODULE_PARM(unit_video, "0-16i");
+MODULE_PARM_DESC(unit_video,
+  "Force use of specific minor number(s). 0 is not allowed.");
+MODULE_PARM(remove_zeros, "i");
+MODULE_PARM_DESC(remove_zeros,
+  "Remove zero-padding from uncompressed incoming data");
 
-MODULE_AUTHOR( DRIVER_AUTHOR );
-MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
 
 static struct usb_driver ov511_driver;
 
-/* I know, I know, global variables suck. This is only a temporary hack */
-int output_offset;
+static struct ov51x_decomp_ops *ov511_decomp_ops;
+static struct ov51x_decomp_ops *ov511_mmx_decomp_ops;
+static struct ov51x_decomp_ops *ov518_decomp_ops;
+static struct ov51x_decomp_ops *ov518_mmx_decomp_ops;
+
+/* Number of times to retry a failed I2C transaction. Increase this if you
+ * are getting "Failed to read sensor ID..." */
+static int i2c_detect_tries = 5;
+
+/* MMX support is present in kernel and CPU. Checked upon decomp module load. */
+static int ov51x_mmx_available;
+
+/* Function prototypes */
+static void ov51x_clear_snapshot(struct usb_ov511 *);
+static int ov51x_check_snapshot(struct usb_ov511 *);
+static inline int sensor_get_picture(struct usb_ov511 *, 
+				     struct video_picture *);
+static int sensor_get_exposure(struct usb_ov511 *, unsigned char *);
+static int ov511_control_ioctl(struct inode *, struct file *, unsigned int,
+			       unsigned long);
 
 /**********************************************************************
  * List of known OV511-based cameras
  **********************************************************************/
 
 static struct cam_list clist[] = {
-	{   0, "generic model (no ID)" },
+	{   0, "Generic Camera (no ID)" },
+	{   1, "Mustek WCam 3X" },
 	{   3, "D-Link DSB-C300" },
-	{   4, "generic OV511/OV7610" },
+	{   4, "Generic OV511/OV7610" },
 	{   5, "Puretek PT-6007" },
+	{   6, "Lifeview USB Life TV (NTSC)" },
 	{  21, "Creative Labs WebCam 3" },
 	{  36, "Koala-Cam" },
-	{  38, "Lifeview USB Life TV" },	/* No support yet! */
+	{  38, "Lifeview USB Life TV" },
+	{  41, "Samsung Anycam MPC-M10" },
+	{  43, "Mtekvision Zeca MV402" },
+	{  46, "Suma eON" },
 	{ 100, "Lifeview RoboCam" },
 	{ 102, "AverMedia InterCam Elite" },
 	{ 112, "MediaForte MV300" },	/* or OV7110 evaluation kit */
@@ -182,9 +335,11 @@
 };
 
 static __devinitdata struct usb_device_id device_table [] = {
-	{ USB_DEVICE(0x05a9, 0x0511) },  /* OV511 */
-	{ USB_DEVICE(0x05a9, 0xA511) },  /* OV511+ */
-	{ USB_DEVICE(0x0813, 0x0002) },  /* Intel Play Me2Cam OV511+ */
+	{ USB_DEVICE(VEND_OMNIVISION, PROD_OV511) },
+	{ USB_DEVICE(VEND_OMNIVISION, PROD_OV511PLUS) },
+	{ USB_DEVICE(VEND_OMNIVISION, PROD_OV518) },
+	{ USB_DEVICE(VEND_OMNIVISION, PROD_OV518PLUS) },
+	{ USB_DEVICE(VEND_MATTEL, PROD_ME2CAM) },
 	{ }  /* Terminating entry */
 };
 
@@ -212,6 +367,11 @@
 };
 #endif
 
+static unsigned char yQuanTable511[] = OV511_YQUANTABLE;
+static unsigned char uvQuanTable511[] = OV511_UVQUANTABLE;
+static unsigned char yQuanTable518[] = OV518_YQUANTABLE;
+static unsigned char uvQuanTable518[] = OV518_UVQUANTABLE;
+
 /**********************************************************************
  *
  * Memory management
@@ -231,7 +391,8 @@
 /* Given PGD from the address space's page table, return the kernel
  * virtual mapping of the physical memory mapped at ADR.
  */
-static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr)
+static inline unsigned long 
+uvirt_to_kva(pgd_t *pgd, unsigned long adr)
 {
 	unsigned long ret = 0UL;
 	pmd_t *pmd;
@@ -243,7 +404,8 @@
 			ptep = pte_offset(pmd, adr);
 			pte = *ptep;
 			if (pte_present(pte)) {
-				ret = (unsigned long) page_address(pte_page(pte));
+				ret = (unsigned long) 
+				      page_address(pte_page(pte));
 				ret |= (adr & (PAGE_SIZE - 1));
 			}
 		}
@@ -256,7 +418,8 @@
  * This is used when initializing the contents of the
  * area and marking the pages as reserved.
  */
-static inline unsigned long kvirt_to_pa(unsigned long adr)
+static inline unsigned long 
+kvirt_to_pa(unsigned long adr)
 {
 	unsigned long va, kva, ret;
 
@@ -266,7 +429,8 @@
 	return ret;
 }
 
-static void *rvmalloc(unsigned long size)
+static void *
+rvmalloc(unsigned long size)
 {
 	void *mem;
 	unsigned long adr, page;
@@ -294,7 +458,8 @@
 	return mem;
 }
 
-static void rvfree(void *mem, unsigned long size)
+static void 
+rvfree(void *mem, unsigned long size)
 {
 	unsigned long adr, page;
 
@@ -329,70 +494,90 @@
 
 #define YES_NO(x) ((x) ? "yes" : "no")
 
-static int ov511_read_proc(char *page, char **start, off_t off,
-                          int count, int *eof, void *data)
+/* /proc/video/ov511/<minor#>/info */
+static int 
+ov511_read_proc_info(char *page, char **start, off_t off, int count, int *eof,
+		     void *data)
 {
 	char *out = page;
 	int i, j, len;
 	struct usb_ov511 *ov511 = data;
+	struct video_picture p;
+	unsigned char exp;
+
+	if (!ov511 || !ov511->dev)
+		return -ENODEV;
+
+	sensor_get_picture(ov511, &p);
+	sensor_get_exposure(ov511, &exp);
 
 	/* IMPORTANT: This output MUST be kept under PAGE_SIZE
 	 *            or we need to get more sophisticated. */
 
-	out += sprintf (out, "driver_version  : %s\n", DRIVER_VERSION);
-	out += sprintf (out, "custom_id       : %d\n", ov511->customid);
-	out += sprintf (out, "model           : %s\n", ov511->desc ?
-		clist[ov511->desc].description : "unknown");
-	out += sprintf (out, "streaming       : %s\n", YES_NO (ov511->streaming));
-	out += sprintf (out, "grabbing        : %s\n", YES_NO (ov511->grabbing));
-	out += sprintf (out, "compress        : %s\n", YES_NO (ov511->compress));
-	out += sprintf (out, "subcapture      : %s\n", YES_NO (ov511->sub_flag));
-	out += sprintf (out, "sub_size        : %d %d %d %d\n",
-		ov511->subx, ov511->suby, ov511->subw, ov511->subh);
-	out += sprintf (out, "data_format     : %s\n", force_rgb ? "RGB" : "BGR");
-	out += sprintf (out, "brightness      : %d\n", ov511->brightness >> 8);
-	out += sprintf (out, "colour          : %d\n", ov511->colour >> 8);
-	out += sprintf (out, "contrast        : %d\n", ov511->contrast >> 8);
-	out += sprintf (out, "num_frames      : %d\n", OV511_NUMFRAMES);
+	out += sprintf(out, "driver_version  : %s\n", DRIVER_VERSION);
+	out += sprintf(out, "custom_id       : %d\n", ov511->customid);
+	out += sprintf(out, "model           : %s\n", ov511->desc ?
+		       clist[ov511->desc].description : "unknown");
+	out += sprintf(out, "streaming       : %s\n", YES_NO(ov511->streaming));
+	out += sprintf(out, "grabbing        : %s\n", YES_NO(ov511->grabbing));
+	out += sprintf(out, "compress        : %s\n", YES_NO(ov511->compress));
+	out += sprintf(out, "subcapture      : %s\n", YES_NO(ov511->sub_flag));
+	out += sprintf(out, "sub_size        : %d %d %d %d\n",
+		       ov511->subx, ov511->suby, ov511->subw, ov511->subh);
+	out += sprintf(out, "data_format     : %s\n",
+		       force_rgb ? "RGB" : "BGR");
+	out += sprintf(out, "brightness      : %d\n", p.brightness >> 8);
+	out += sprintf(out, "colour          : %d\n", p.colour >> 8);
+	out += sprintf(out, "contrast        : %d\n", p.contrast >> 8);
+	out += sprintf(out, "hue             : %d\n", p.hue >> 8);
+	out += sprintf(out, "exposure        : %d\n", exp);
+	out += sprintf(out, "num_frames      : %d\n", OV511_NUMFRAMES);
 	for (i = 0; i < OV511_NUMFRAMES; i++) {
-		out += sprintf (out, "frame           : %d\n", i);
-		out += sprintf (out, "  depth         : %d\n",
-			ov511->frame[i].depth);
-		out += sprintf (out, "  size          : %d %d\n",
-			ov511->frame[i].width, ov511->frame[i].height);
-		out += sprintf (out, "  format        : ");
+		out += sprintf(out, "frame           : %d\n", i);
+		out += sprintf(out, "  depth         : %d\n",
+			       ov511->frame[i].depth);
+		out += sprintf(out, "  size          : %d %d\n",
+			       ov511->frame[i].width, ov511->frame[i].height);
+		out += sprintf(out, "  format        : ");
 		for (j = 0; plist[j].num >= 0; j++) {
 			if (plist[j].num == ov511->frame[i].format) {
-				out += sprintf (out, "%s\n", plist[j].name);
+				out += sprintf(out, "%s\n", plist[j].name);
 				break;
 			}
 		}
 		if (plist[j].num < 0)
-			out += sprintf (out, "unknown\n");
-		out += sprintf (out, "  segsize       : %d\n",
-			ov511->frame[i].segsize);
-		out += sprintf (out, "  data_buffer   : 0x%p\n",
-			ov511->frame[i].data);
-	}
-	out += sprintf (out, "snap_enabled    : %s\n", YES_NO (ov511->snap_enabled));
-	out += sprintf (out, "bridge          : %s\n",
-		ov511->bridge == BRG_OV511 ? "OV511" :
-		ov511->bridge == BRG_OV511PLUS ? "OV511+" :
-		"unknown");
-	out += sprintf (out, "sensor          : %s\n",
-		ov511->sensor == SEN_OV6620 ? "OV6620" :
-		ov511->sensor == SEN_OV7610 ? "OV7610" :
-		ov511->sensor == SEN_OV7620 ? "OV7620" :
-		ov511->sensor == SEN_OV7620AE ? "OV7620AE" :
-		"unknown");
-	out += sprintf (out, "packet_size     : %d\n", ov511->packet_size);
-	out += sprintf (out, "framebuffer     : 0x%p\n", ov511->fbuf);
-	
+			out += sprintf(out, "unknown\n");
+		out += sprintf(out, "  data_buffer   : 0x%p\n",
+			       ov511->frame[i].data);
+	}
+	out += sprintf(out, "snap_enabled    : %s\n",
+		       YES_NO(ov511->snap_enabled));
+	out += sprintf(out, "bridge          : %s\n",
+		       ov511->bridge == BRG_OV511 ? "OV511" :
+			ov511->bridge == BRG_OV511PLUS ? "OV511+" :
+			ov511->bridge == BRG_OV518 ? "OV518" :
+			ov511->bridge == BRG_OV518PLUS ? "OV518+" :
+			"unknown");
+	out += sprintf(out, "sensor          : %s\n",
+		       ov511->sensor == SEN_OV6620 ? "OV6620" :
+			ov511->sensor == SEN_OV6630 ? "OV6630" :
+			ov511->sensor == SEN_OV7610 ? "OV7610" :
+			ov511->sensor == SEN_OV7620 ? "OV7620" :
+			ov511->sensor == SEN_OV7620AE ? "OV7620AE" :
+			ov511->sensor == SEN_OV8600 ? "OV8600" :
+			ov511->sensor == SEN_KS0127 ? "KS0127" :
+			ov511->sensor == SEN_KS0127B ? "KS0127B" :
+			ov511->sensor == SEN_SAA7111A ? "SAA7111A" :
+			"unknown");
+	out += sprintf(out, "packet_size     : %d\n", ov511->packet_size);
+	out += sprintf(out, "framebuffer     : 0x%p\n", ov511->fbuf);
+
 	len = out - page;
 	len -= off;
 	if (len < count) {
 		*eof = 1;
-		if (len <= 0) return 0;
+		if (len <= 0)
+			return 0;
 	} else
 		len = count;
 
@@ -401,69 +586,160 @@
 	return len;
 }
 
-static int ov511_write_proc(struct file *file, const char *buffer,
-                           unsigned long count, void *data)
+/* /proc/video/ov511/<minor#>/button
+ *
+ * When the camera's button is pressed, the output of this will change from a
+ * 0 to a 1 (ASCII). It will retain this value until it is read, after which
+ * it will reset to zero.
+ * 
+ * SECURITY NOTE: Since reading this file can change the state of the snapshot
+ * status, it is important for applications that open it to keep it locked
+ * against access by other processes, using flock() or a similar mechanism. No
+ * locking is provided by this driver.
+ */
+static int 
+ov511_read_proc_button(char *page, char **start, off_t off, int count, int *eof,
+		       void *data)
 {
-	return -EINVAL;
+	char *out = page;
+	int len, status;
+	struct usb_ov511 *ov511 = data;
+
+	if (!ov511 || !ov511->dev)
+		return -ENODEV;
+
+	status = ov51x_check_snapshot(ov511);
+	out += sprintf(out, "%d", status);
+
+	if (status)
+		ov51x_clear_snapshot(ov511);
+
+	len = out - page;
+	len -= off;
+	if (len < count) {
+		*eof = 1;
+		if (len <= 0)
+			return 0;
+	} else {
+		len = count;
+	}
+
+	*start = page + off;
+
+	return len;
 }
 
-static void create_proc_ov511_cam (struct usb_ov511 *ov511)
+static void 
+create_proc_ov511_cam(struct usb_ov511 *ov511)
 {
-	char name[7];
-	struct proc_dir_entry *ent;
-	
+	char dirname[4];
+
 	if (!ov511_proc_entry || !ov511)
 		return;
 
-	sprintf(name, "video%d", ov511->vdev.minor);
-	PDEBUG (4, "creating /proc/video/ov511/%s", name);
-	
-	ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, ov511_proc_entry);
+	/* Create per-device directory */
+	sprintf(dirname, "%d", ov511->vdev.minor);
+	PDEBUG(4, "creating /proc/video/ov511/%s/", dirname);
+	ov511->proc_devdir = create_proc_entry(dirname, S_IFDIR,
+		ov511_proc_entry);
+	if (!ov511->proc_devdir)
+		return;
 
-	if (!ent)
+	/* Create "info" entry (human readable device information) */
+	PDEBUG(4, "creating /proc/video/ov511/%s/info", dirname);
+	ov511->proc_info = create_proc_read_entry("info",
+		S_IFREG|S_IRUGO|S_IWUSR, ov511->proc_devdir,
+		ov511_read_proc_info, ov511);
+	if (!ov511->proc_info)
 		return;
 
-	ent->data = ov511;
-	ent->read_proc = ov511_read_proc;
-	ent->write_proc = ov511_write_proc;
-	ov511->proc_entry = ent;
+	/* Don't create it if old snapshot mode on (would cause race cond.) */
+	if (!snapshot) {
+		/* Create "button" entry (snapshot button status) */
+		PDEBUG(4, "creating /proc/video/ov511/%s/button", dirname);
+		ov511->proc_button = create_proc_read_entry("button",
+			S_IFREG|S_IRUGO|S_IWUSR, ov511->proc_devdir,
+			ov511_read_proc_button, ov511);
+		if (!ov511->proc_button)
+			return;
+	}
+
+	/* Create "control" entry (ioctl() interface) */
+	PDEBUG(4, "creating /proc/video/ov511/%s/control", dirname);
+	lock_kernel();
+	ov511->proc_control = create_proc_entry("control",
+		S_IFREG|S_IRUGO|S_IWUSR, ov511->proc_devdir);
+	if (!ov511->proc_control) {
+		unlock_kernel();
+		return;
+	}
+	ov511->proc_control->proc_fops->ioctl = ov511_control_ioctl;
+	ov511->proc_control->data = ov511;
+	unlock_kernel();
 }
 
-static void destroy_proc_ov511_cam (struct usb_ov511 *ov511)
+static void 
+destroy_proc_ov511_cam(struct usb_ov511 *ov511)
 {
-	char name[7];
+	char dirname[4];
 	
-	if (!ov511 || !ov511->proc_entry)
+	if (!ov511 || !ov511->proc_devdir)
 		return;
-	
-	sprintf(name, "video%d", ov511->vdev.minor);
-	PDEBUG (4, "destroying %s", name);
-	remove_proc_entry(name, ov511_proc_entry);
-	ov511->proc_entry = NULL;
+
+	sprintf(dirname, "%d", ov511->vdev.minor);
+
+	/* Destroy "control" entry */
+	if (ov511->proc_control) {
+		PDEBUG(4, "destroying /proc/video/ov511/%s/control", dirname);
+		remove_proc_entry("control", ov511->proc_devdir);
+		ov511->proc_control = NULL;
+	}
+
+	/* Destroy "button" entry */
+	if (ov511->proc_button) {
+		PDEBUG(4, "destroying /proc/video/ov511/%s/button", dirname);
+		remove_proc_entry("button", ov511->proc_devdir);
+		ov511->proc_button = NULL;
+	}
+
+	/* Destroy "info" entry */
+	if (ov511->proc_info) {
+		PDEBUG(4, "destroying /proc/video/ov511/%s/info", dirname);
+		remove_proc_entry("info", ov511->proc_devdir);
+		ov511->proc_info = NULL;
+	}
+
+	/* Destroy per-device directory */
+	PDEBUG(4, "destroying /proc/video/ov511/%s/", dirname);
+	remove_proc_entry(dirname, ov511_proc_entry);
+	ov511->proc_devdir = NULL;
 }
 
-static void proc_ov511_create(void)
+static void 
+proc_ov511_create(void)
 {
 	/* No current standard here. Alan prefers /proc/video/ as it keeps
 	 * /proc "less cluttered than /proc/randomcardifoundintheshed/"
 	 * -claudio
 	 */
 	if (video_proc_entry == NULL) {
-		err("Unable to initialise /proc/video/ov511");
+		err("Error: /proc/video/ does not exist");
 		return;
 	}
 
-	ov511_proc_entry = create_proc_entry("ov511", S_IFDIR, video_proc_entry);
+	ov511_proc_entry = create_proc_entry("ov511", S_IFDIR,
+					     video_proc_entry);
 
 	if (ov511_proc_entry)
 		ov511_proc_entry->owner = THIS_MODULE;
 	else
-		err("Unable to initialise /proc/ov511");
+		err("Unable to create /proc/video/ov511");
 }
 
-static void proc_ov511_destroy(void)
+static void 
+proc_ov511_destroy(void)
 {
-	PDEBUG (3, "removing /proc/video/ov511");
+	PDEBUG(3, "removing /proc/video/ov511");
 
 	if (ov511_proc_entry == NULL)
 		return;
@@ -474,23 +750,22 @@
 
 /**********************************************************************
  *
- * Camera interface
+ * Register I/O
  *
  **********************************************************************/
 
-static int ov511_reg_write(struct usb_device *dev,
-			   unsigned char reg,
-			   unsigned char value)
+static int 
+ov511_reg_write(struct usb_device *dev, unsigned char reg, unsigned char value)
 {
 	int rc;
 
-	rc = usb_control_msg(dev,
-		usb_sndctrlpipe(dev, 0),
-		2 /* REG_IO */,
-		USB_TYPE_CLASS | USB_RECIP_DEVICE,
-		0, (__u16)reg, &value, 1, HZ);	
+	PDEBUG(5, "0x%02X:0x%02X", reg, value);
 
-	PDEBUG(5, "reg write: 0x%02X:0x%02X, 0x%x", reg, value, rc);
+	rc = usb_control_msg(dev,
+			     usb_sndctrlpipe(dev, 0),
+			     2 /* REG_IO */,
+			     USB_TYPE_CLASS | USB_RECIP_DEVICE,
+			     0, (__u16)reg, &value, 1, HZ);	
 
 	if (rc < 0)
 		err("reg write: error %d", rc);
@@ -499,18 +774,19 @@
 }
 
 /* returns: negative is error, pos or zero is data */
-static int ov511_reg_read(struct usb_device *dev, unsigned char reg)
+static int 
+ov511_reg_read(struct usb_device *dev, unsigned char reg)
 {
 	int rc;
 	unsigned char buffer[1];
 
 	rc = usb_control_msg(dev,
-		usb_rcvctrlpipe(dev, 0),
-		2 /* REG_IO */,
-		USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE,
-		0, (__u16)reg, buffer, 1, HZ);
+			     usb_rcvctrlpipe(dev, 0),
+			     2 /* REG_IO */,
+			     USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE,
+			     0, (__u16)reg, buffer, 1, HZ);
                                
-	PDEBUG(5, "reg read: 0x%02X:0x%02X", reg, buffer[0]);
+	PDEBUG(5, "0x%02X:0x%02X", reg, buffer[0]);
 	
 	if (rc < 0) {
 		err("reg read: error %d", rc);
@@ -520,18 +796,197 @@
 	}
 }
 
-static int ov511_i2c_write(struct usb_device *dev,
-			   unsigned char reg,
-			   unsigned char value)
+/*
+ * Writes bits at positions specified by mask to a reg. Bits that are in
+ * the same position as 1's in "mask" are cleared and set to "value". Bits
+ * that are in the same position as 0's in "mask" are preserved, regardless 
+ * of their respective state in "value".
+ */
+static int 
+ov511_reg_write_mask(struct usb_device *dev,
+		     unsigned char reg,
+		     unsigned char value,
+		     unsigned char mask)
+{
+	int ret;
+	unsigned char oldval, newval;
+
+	ret = ov511_reg_read(dev, reg);
+	if (ret < 0)
+		return ret;
+
+	oldval = (unsigned char) ret;
+	oldval &= (~mask);		/* Clear the masked bits */
+	value &= mask;			/* Enforce mask on value */
+	newval = oldval | value;	/* Set the desired bits */
+
+	return (ov511_reg_write(dev, reg, newval));
+}
+
+/* Writes multiple (n) values to a single register. Only valid with certain
+ * registers (0x30 and 0xc4 - 0xce). Used for writing 16 and 24-bit values. */
+static int 
+ov518_reg_write_multi(struct usb_device *dev,
+		      unsigned char reg,
+		      unsigned char *values,
+		      int n)
+{
+	int rc;
+
+	PDEBUG(5, "0x%02X:[multiple], n=%d", reg, n);  // FIXME
+
+	if (values == NULL) {
+		err("reg write multiple: NULL buffer");
+		return -EINVAL;
+	}
+
+	rc = usb_control_msg(dev,
+			     usb_sndctrlpipe(dev, 0),
+			     2 /* REG_IO */,
+			     USB_TYPE_CLASS | USB_RECIP_DEVICE,
+			     0, (__u16)reg, values, n, HZ);	
+
+	if (rc < 0)
+		err("reg write multiple: error %d", rc);
+
+	return rc;
+}
+
+static int 
+ov511_upload_quan_tables(struct usb_device *dev)
+{
+	unsigned char *pYTable = yQuanTable511;
+	unsigned char *pUVTable = uvQuanTable511;
+	unsigned char val0, val1;
+	int i, rc, reg = OV511_OMNICE_Y_LUT_BEGIN;
+
+	PDEBUG(4, "Uploading quantization tables");
+
+	for (i = 0; i < OV511_QUANTABLESIZE / 2; i++)
+	{
+		if (ENABLE_Y_QUANTABLE)
+		{
+			val0 = *pYTable++;
+			val1 = *pYTable++;
+			val0 &= 0x0f;
+			val1 &= 0x0f;
+			val0 |= val1 << 4;
+			rc = ov511_reg_write(dev, reg, val0);
+			if (rc < 0)
+				return rc;
+		}
+
+		if (ENABLE_UV_QUANTABLE)
+		{
+			val0 = *pUVTable++;
+			val1 = *pUVTable++;
+			val0 &= 0x0f;
+			val1 &= 0x0f;
+			val0 |= val1 << 4;
+			rc = ov511_reg_write(dev, reg + OV511_QUANTABLESIZE / 2,
+				val0);
+			if (rc < 0)
+				return rc;
+		}
+
+		reg++;
+	}
+
+	return 0;
+}
+
+/* OV518 quantization tables are 8x4 (instead of 8x8) */
+static int 
+ov518_upload_quan_tables(struct usb_device *dev)
+{
+	unsigned char *pYTable = yQuanTable518;
+	unsigned char *pUVTable = uvQuanTable518;
+	unsigned char val0, val1;
+	int i, rc, reg = OV511_OMNICE_Y_LUT_BEGIN;
+
+	PDEBUG(4, "Uploading quantization tables");
+
+	for (i = 0; i < OV518_QUANTABLESIZE / 2; i++)
+	{
+		if (ENABLE_Y_QUANTABLE)
+		{
+			val0 = *pYTable++;
+			val1 = *pYTable++;
+			val0 &= 0x0f;
+			val1 &= 0x0f;
+			val0 |= val1 << 4;
+			rc = ov511_reg_write(dev, reg, val0);
+			if (rc < 0)
+				return rc;
+		}
+
+		if (ENABLE_UV_QUANTABLE)
+		{
+			val0 = *pUVTable++;
+			val1 = *pUVTable++;
+			val0 &= 0x0f;
+			val1 &= 0x0f;
+			val0 |= val1 << 4;
+			rc = ov511_reg_write(dev, reg + OV518_QUANTABLESIZE / 2,
+				val0);
+			if (rc < 0)
+				return rc;
+		}
+
+		reg++;
+	}
+
+	return 0;
+}
+
+/* NOTE: Do not call this function directly!
+ * The OV518 I2C I/O procedure is different, hence, this function.
+ * This is normally only called from ov51x_i2c_write(). Note that this function
+ * always succeeds regardless of whether the sensor is present and working.
+ */
+static int 
+ov518_i2c_write_internal(struct usb_device *dev,
+			 unsigned char reg,
+			 unsigned char value)
+{
+	int rc;
+
+	PDEBUG(5, "0x%02X:0x%02X", reg, value);
+
+	/* Select camera register */
+	rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_3_BYTE, reg);
+	if (rc < 0) goto error;
+
+	/* Write "value" to I2C data port of OV511 */
+	rc = ov511_reg_write(dev, OV511_REG_I2C_DATA_PORT, value);
+	if (rc < 0) goto error;
+
+	/* Initiate 3-byte write cycle */
+	rc = ov511_reg_write(dev, OV518_REG_I2C_CONTROL, 0x01);
+	if (rc < 0) goto error;
+
+	return 0;
+
+error:
+	err("ov518 i2c write: error %d", rc);
+	return rc;
+}
+
+/* NOTE: Do not call this function directly! */
+static int 
+ov511_i2c_write_internal(struct usb_device *dev,
+			 unsigned char reg,
+			 unsigned char value)
 {
 	int rc, retries;
 
-	PDEBUG(5, "i2c write: 0x%02X:0x%02X", reg, value);
+	PDEBUG(5, "0x%02X:0x%02X", reg, value);
 
 	/* Three byte write cycle */
 	for (retries = OV511_I2C_RETRIES; ; ) {
 		/* Select camera register */
-		rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_3_BYTE, reg);
+		rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_3_BYTE,
+				     reg);
 		if (rc < 0) goto error;
 
 		/* Write "value" to I2C data port of OV511 */
@@ -566,15 +1021,51 @@
 	return rc;
 }
 
-/* returns: negative is error, pos or zero is data */
-static int ov511_i2c_read(struct usb_device *dev, unsigned char reg)
+/* NOTE: Do not call this function directly!
+ * The OV518 I2C I/O procedure is different, hence, this function.
+ * This is normally only called from ov51x_i2c_read(). Note that this function
+ * always succeeds regardless of whether the sensor is present and working.
+ */
+static int 
+ov518_i2c_read_internal(struct usb_device *dev, unsigned char reg)
+{
+	int rc, value;
+
+	/* Select camera register */
+	rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_2_BYTE, reg);
+	if (rc < 0) goto error;
+
+	/* Initiate 2-byte write cycle */
+	rc = ov511_reg_write(dev, OV518_REG_I2C_CONTROL, 0x03);
+	if (rc < 0) goto error;
+
+	/* Initiate 2-byte read cycle */
+	rc = ov511_reg_write(dev, OV518_REG_I2C_CONTROL, 0x05);
+	if (rc < 0) goto error;
+
+	value = ov511_reg_read(dev, OV511_REG_I2C_DATA_PORT);
+
+	PDEBUG(5, "0x%02X:0x%02X", reg, value);
+
+	return value;
+
+error:
+	err("ov518 i2c read: error %d", rc);
+	return rc;
+}
+
+/* NOTE: Do not call this function directly!
+ * returns: negative is error, pos or zero is data */
+static int 
+ov511_i2c_read_internal(struct usb_device *dev, unsigned char reg)
 {
 	int rc, value, retries;
 
 	/* Two byte write cycle */
 	for (retries = OV511_I2C_RETRIES; ; ) {
 		/* Select camera register */
-		rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_2_BYTE, reg);
+		rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_2_BYTE,
+				     reg);
 		if (rc < 0) goto error;
 
 		/* Initiate 2-byte write cycle */
@@ -624,9 +1115,9 @@
 
 	value = ov511_reg_read(dev, OV511_REG_I2C_DATA_PORT);
 
-	PDEBUG(5, "i2c read: 0x%02X:0x%02X", reg, value);
+	PDEBUG(5, "0x%02X:0x%02X", reg, value);
 		
-	/* This is needed to make ov511_i2c_write() work */
+	/* This is needed to make ov51x_i2c_write() work */
 	rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x05);
 	if (rc < 0)
 		goto error;
@@ -638,19 +1129,207 @@
 	return rc;
 }
 
-static int ov511_write_regvals(struct usb_device *dev,
-			       struct ov511_regvals * pRegvals)
+/* returns: negative is error, pos or zero is data */
+static int 
+ov51x_i2c_read(struct usb_ov511 *ov511, unsigned char reg)
+{
+	int rc;
+	struct usb_device *dev = ov511->dev;
+
+	down(&ov511->i2c_lock);
+
+	if (dev->descriptor.idProduct == PROD_OV518 ||
+	    dev->descriptor.idProduct == PROD_OV518PLUS)
+		rc = ov518_i2c_read_internal(dev, reg);
+	else
+		rc = ov511_i2c_read_internal(dev, reg);
+
+	up(&ov511->i2c_lock);
+
+	return rc;
+}
+
+static int 
+ov51x_i2c_write(struct usb_ov511 *ov511,
+		unsigned char reg,
+		unsigned char value)
+{
+	int rc;
+	struct usb_device *dev = ov511->dev;
+
+	down(&ov511->i2c_lock);
+
+	if (dev->descriptor.idProduct == PROD_OV518 ||
+	    dev->descriptor.idProduct == PROD_OV518PLUS)
+		rc = ov518_i2c_write_internal(dev, reg, value);
+	else
+		rc = ov511_i2c_write_internal(dev, reg, value);
+
+	up(&ov511->i2c_lock);
+
+	return rc;
+}
+
+/* Do not call this function directly! */
+static int 
+ov51x_i2c_write_mask_internal(struct usb_device *dev,
+			      unsigned char reg,
+			      unsigned char value,
+			      unsigned char mask)
+{
+	int rc;
+	unsigned char oldval, newval;
+
+	if (mask == 0xff) {
+		newval = value;
+	} else {
+		if (dev->descriptor.idProduct == PROD_OV518 ||
+		    dev->descriptor.idProduct == PROD_OV518PLUS)
+			rc = ov518_i2c_read_internal(dev, reg);
+		else
+			rc = ov511_i2c_read_internal(dev, reg);
+		if (rc < 0)
+			return rc;
+
+		oldval = (unsigned char) rc;
+		oldval &= (~mask);		/* Clear the masked bits */
+		value &= mask;			/* Enforce mask on value */
+		newval = oldval | value;	/* Set the desired bits */
+	}
+
+	if (dev->descriptor.idProduct == PROD_OV518 ||
+	    dev->descriptor.idProduct == PROD_OV518PLUS)
+		return (ov518_i2c_write_internal(dev, reg, newval));
+	else
+		return (ov511_i2c_write_internal(dev, reg, newval));
+}
+
+/* Writes bits at positions specified by mask to an I2C reg. Bits that are in
+ * the same position as 1's in "mask" are cleared and set to "value". Bits
+ * that are in the same position as 0's in "mask" are preserved, regardless 
+ * of their respective state in "value".
+ */
+static int 
+ov51x_i2c_write_mask(struct usb_ov511 *ov511,
+		     unsigned char reg,
+		     unsigned char value,
+		     unsigned char mask)
+{
+	int rc;
+	struct usb_device *dev = ov511->dev;
+
+	down(&ov511->i2c_lock);
+	rc = ov51x_i2c_write_mask_internal(dev, reg, value, mask);
+	up(&ov511->i2c_lock);
+
+	return rc;
+}
+
+/* Write to a specific I2C slave ID and register, using the specified mask */
+static int 
+ov51x_i2c_write_slave(struct usb_ov511 *ov511,
+		      unsigned char slave,
+		      unsigned char reg,
+		      unsigned char value,
+		      unsigned char mask)
+{
+	int rc = 0;
+	struct usb_device *dev = ov511->dev;
+
+	down(&ov511->i2c_lock);
+
+	/* Set new slave IDs */
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, slave) < 0) {
+		rc = -EIO;
+		goto out;
+	}
+
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, slave + 1) < 0) {
+		rc = -EIO;
+		goto out;
+	}
+
+	rc = ov51x_i2c_write_mask_internal(dev, reg, value, mask);
+	/* Don't bail out yet if error; IDs must be restored */
+
+	/* Restore primary IDs */
+	slave = ov511->primary_i2c_slave;
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, slave) < 0) {
+		rc = -EIO;
+		goto out;
+	}
+
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, slave + 1) < 0) {
+		rc = -EIO;
+		goto out;
+	}
+
+out:
+	up(&ov511->i2c_lock);
+	return rc;
+}
+
+/* Read from a specific I2C slave ID and register */
+static int 
+ov51x_i2c_read_slave(struct usb_ov511 *ov511,
+		     unsigned char slave,
+		     unsigned char reg)
+{
+	int rc;
+	struct usb_device *dev = ov511->dev;
+
+	down(&ov511->i2c_lock);
+
+	/* Set new slave IDs */
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, slave) < 0) {
+		rc = -EIO;
+		goto out;
+	}
+
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, slave + 1) < 0) {
+		rc = -EIO;
+		goto out;
+	}
+
+	if (dev->descriptor.idProduct == PROD_OV518 ||
+	    dev->descriptor.idProduct == PROD_OV518PLUS)
+		rc = ov518_i2c_read_internal(dev, reg);
+	else
+		rc = ov511_i2c_read_internal(dev, reg);
+	/* Don't bail out yet if error; IDs must be restored */
+
+	/* Restore primary IDs */
+	slave = ov511->primary_i2c_slave;
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, slave) < 0) {
+		rc = -EIO;
+		goto out;
+	}
+
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, slave + 1) < 0) {
+		rc = -EIO;
+		goto out;
+	}
+
+out:
+	up(&ov511->i2c_lock);
+	return rc;
+}
+
+static int 
+ov511_write_regvals(struct usb_ov511 *ov511,
+		    struct ov511_regvals * pRegvals)
 {
 	int rc;
+	struct usb_device *dev = ov511->dev;
 
 	while (pRegvals->bus != OV511_DONE_BUS) {
 		if (pRegvals->bus == OV511_REG_BUS) {
 			if ((rc = ov511_reg_write(dev, pRegvals->reg,
-			                           pRegvals->val)) < 0)
+			                          pRegvals->val)) < 0)
 				goto error;
 		} else if (pRegvals->bus == OV511_I2C_BUS) {
-			if ((rc = ov511_i2c_write(dev, pRegvals->reg, 
-			                           pRegvals->val)) < 0)
+			if ((rc = ov51x_i2c_write(ov511, pRegvals->reg, 
+			                          pRegvals->val)) < 0)
 				goto error;
 		} else {
 			err("Bad regval array");
@@ -667,65 +1346,92 @@
 }
 
 #ifdef OV511_DEBUG 
-static void ov511_dump_i2c_range(struct usb_device *dev, int reg1, int regn)
+static void 
+ov511_dump_i2c_range(struct usb_ov511 *ov511, int reg1, int regn)
 {
 	int i;
 	int rc;
-	for(i = reg1; i <= regn; i++) {
-		rc = ov511_i2c_read(dev, i);
-		PDEBUG(1, "OV7610[0x%X] = 0x%X", i, rc);
+	for (i = reg1; i <= regn; i++) {
+		rc = ov51x_i2c_read(ov511, i);
+		info("OV7610[0x%X] = 0x%X", i, rc);
 	}
 }
 
-static void ov511_dump_i2c_regs(struct usb_device *dev)
+static void 
+ov51x_dump_i2c_regs(struct usb_ov511 *ov511)
 {
-	PDEBUG(3, "I2C REGS");
-	ov511_dump_i2c_range(dev, 0x00, 0x7C);
+	info("I2C REGS");
+	ov511_dump_i2c_range(ov511, 0x00, 0x7C);
 }
 
-#if 0
-static void ov511_dump_reg_range(struct usb_device *dev, int reg1, int regn)
+static void 
+ov511_dump_reg_range(struct usb_device *dev, int reg1, int regn)
 {
 	int i;
 	int rc;
-	for(i = reg1; i <= regn; i++) {
+	for (i = reg1; i <= regn; i++) {
 	  rc = ov511_reg_read(dev, i);
-	  PDEBUG(1, "OV511[0x%X] = 0x%X", i, rc);
+	  info("OV511[0x%X] = 0x%X", i, rc);
 	}
 }
 
-static void ov511_dump_regs(struct usb_device *dev)
+static void 
+ov511_dump_regs(struct usb_device *dev)
 {
-	PDEBUG(1, "CAMERA INTERFACE REGS");
+	info("CAMERA INTERFACE REGS");
 	ov511_dump_reg_range(dev, 0x10, 0x1f);
-	PDEBUG(1, "DRAM INTERFACE REGS");
+	info("DRAM INTERFACE REGS");
 	ov511_dump_reg_range(dev, 0x20, 0x23);
-	PDEBUG(1, "ISO FIFO REGS");
+	info("ISO FIFO REGS");
 	ov511_dump_reg_range(dev, 0x30, 0x31);
-	PDEBUG(1, "PIO REGS");
+	info("PIO REGS");
 	ov511_dump_reg_range(dev, 0x38, 0x39);
 	ov511_dump_reg_range(dev, 0x3e, 0x3e);
-	PDEBUG(1, "I2C REGS");
+	info("I2C REGS");
 	ov511_dump_reg_range(dev, 0x40, 0x49);
-	PDEBUG(1, "SYSTEM CONTROL REGS");
+	info("SYSTEM CONTROL REGS");
 	ov511_dump_reg_range(dev, 0x50, 0x55);
 	ov511_dump_reg_range(dev, 0x5e, 0x5f);
-	PDEBUG(1, "OmniCE REGS");
+	info("OmniCE REGS");
 	ov511_dump_reg_range(dev, 0x70, 0x79);
+	/* NOTE: Quantization tables are not readable. You will get the value
+	 * in reg. 0x79 for every table register */
 	ov511_dump_reg_range(dev, 0x80, 0x9f);
 	ov511_dump_reg_range(dev, 0xa0, 0xbf);
 
 }
 #endif
-#endif
 
-static int ov511_reset(struct usb_device *dev, unsigned char reset_type)
+/**********************************************************************
+ *
+ * Kernel I2C Interface
+ *
+ **********************************************************************/
+
+/* For as-yet unimplemented I2C interface */
+static void 
+call_i2c_clients(struct usb_ov511 *ov511, unsigned int cmd,
+		 void *arg)
+{
+	/* Do nothing */
+}
+
+/*****************************************************************************/
+
+static int 
+ov511_reset(struct usb_ov511 *ov511, unsigned char reset_type)
 {
 	int rc;
-	
+		
+	/* Setting bit 0 not allowed on 518/518Plus */
+	if (ov511->bridge == BRG_OV518 ||
+	    ov511->bridge == BRG_OV518PLUS)
+		reset_type &= 0xfe;
+
 	PDEBUG(4, "Reset: type=0x%X", reset_type);
-	rc = ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, reset_type);
-	rc = ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0);
+
+	rc = ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET, reset_type);
+	rc = ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET, 0);
 
 	if (rc < 0)
 		err("reset: command failed");
@@ -735,29 +1441,150 @@
 
 /* Temporarily stops OV511 from functioning. Must do this before changing
  * registers while the camera is streaming */
-static inline int ov511_stop(struct usb_device *dev)
+static inline int 
+ov511_stop(struct usb_ov511 *ov511)
 {
 	PDEBUG(4, "stopping");
-	return (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x3d));
+	ov511->stopped = 1;	
+	if (ov511->bridge == BRG_OV518 ||
+	    ov511->bridge == BRG_OV518PLUS)
+		return (ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET,
+					0x3a));
+	else
+		return (ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET,
+					0x3d));
+}
+
+/* Restarts OV511 after ov511_stop() is called. Has no effect if it is not
+ * actually stopped (for performance). */
+static inline int 
+ov511_restart(struct usb_ov511 *ov511)
+{
+	if (ov511->stopped) {
+		PDEBUG(4, "restarting");
+		ov511->stopped = 0;	
+
+		/* Reinitialize the stream */
+		if (ov511->bridge == BRG_OV518 ||
+		    ov511->bridge == BRG_OV518PLUS)
+			ov511_reg_write(ov511->dev, 0x2f, 0x80);
+
+		return (ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET,
+					0x00));
+	}
+
+	return 0;
 }
 
-/* Restarts OV511 after ov511_stop() is called */
-static inline int ov511_restart(struct usb_device *dev)
+/* Resets the hardware snapshot button */
+static void 
+ov51x_clear_snapshot(struct usb_ov511 *ov511)
 {
-	PDEBUG(4, "restarting");
-	return (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x00));
+	if (ov511->bridge == BRG_OV511 || ov511->bridge == BRG_OV511PLUS) {
+		ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01);
+		ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x03);
+		ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01);
+	} else if (ov511->bridge == BRG_OV518 ||
+		   ov511->bridge == BRG_OV518PLUS) {
+		warn("snapshot reset not supported yet on OV518(+)");
+	} else {
+		err("clear snap: invalid bridge type");
+	}
+	
+}
+
+/* Checks the status of the snapshot button. Returns 1 if it was pressed since
+ * it was last cleared, and zero in all other cases (including errors) */
+static int 
+ov51x_check_snapshot(struct usb_ov511 *ov511)
+{
+	int ret, status = 0;
+
+	if (ov511->bridge == BRG_OV511 || ov511->bridge == BRG_OV511PLUS) {
+		ret = ov511_reg_read(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT);
+		if (ret < 0) {
+			err("Error checking snspshot status (%d)", ret);
+		} else if (ret & 0x08) {
+			status = 1;
+		}
+	} else if (ov511->bridge == BRG_OV518 ||
+		   ov511->bridge == BRG_OV518PLUS) {
+		warn("snapshot check not supported yet on OV518(+)");
+	} else {
+		err("check snap: invalid bridge type");
+	}
+
+	return status;
 }
 
-static int ov511_set_packet_size(struct usb_ov511 *ov511, int size)
+/* Sets I2C read and write slave IDs. Returns <0 for error */
+static int 
+ov51x_set_slave_ids(struct usb_ov511 *ov511,
+		    unsigned char write_id,
+		    unsigned char read_id)
 {
-	int alt, mult;
+	struct usb_device *dev = ov511->dev;
 
-	if (ov511_stop(ov511->dev) < 0)
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, write_id) < 0)
 		return -EIO;
 
-	mult = size >> 5;
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, read_id) < 0)
+		return -EIO;
 
-	if (ov511->bridge == BRG_OV511) {
+	if (ov511_reset(ov511, OV511_RESET_NOREGS) < 0)
+		return -EIO;
+
+	return 0;
+}
+
+/* This does an initial reset of an OmniVision sensor and ensures that I2C
+ * is synchronized. Returns <0 for failure.
+ */
+static int 
+ov51x_init_ov_sensor(struct usb_ov511 *ov511)
+{
+	int i, success;
+
+	/* Reset the sensor */ 
+	if (ov51x_i2c_write(ov511, 0x12, 0x80) < 0) return -EIO;
+
+	/* Wait for it to initialize */ 
+	schedule_timeout (1 + 150 * HZ / 1000);
+
+	for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) {
+		if ((ov51x_i2c_read(ov511, OV7610_REG_ID_HIGH) == 0x7F) &&
+		    (ov51x_i2c_read(ov511, OV7610_REG_ID_LOW) == 0xA2)) {
+			success = 1;
+			continue;
+		}
+
+		/* Reset the sensor */ 
+		if (ov51x_i2c_write(ov511, 0x12, 0x80) < 0) return -EIO;
+		/* Wait for it to initialize */ 
+		schedule_timeout(1 + 150 * HZ / 1000);
+		/* Dummy read to sync I2C */
+		if (ov51x_i2c_read(ov511, 0x00) < 0) return -EIO;
+	}
+
+	if (!success)
+		return -EIO;
+	
+	PDEBUG(1, "I2C synced in %d attempt(s)", i);
+
+	return 0;
+}
+
+static int 
+ov511_set_packet_size(struct usb_ov511 *ov511, int size)
+{
+	int alt, mult;
+
+	if (ov511_stop(ov511) < 0)
+		return -EIO;
+
+	mult = size >> 5;
+
+	if (ov511->bridge == BRG_OV511) {
 		if (size == 0) alt = OV511_ALT_SIZE_0;
 		else if (size == 257) alt = OV511_ALT_SIZE_257;
 		else if (size == 513) alt = OV511_ALT_SIZE_513;
@@ -780,6 +1607,20 @@
 			err("Set packet size: invalid size (%d)", size);
 			return -EINVAL;
 		}
+	} else if (ov511->bridge == BRG_OV518 ||
+		   ov511->bridge == BRG_OV518PLUS) {
+		if (size == 0) alt = OV518_ALT_SIZE_0;
+		else if (size == 128) alt = OV518_ALT_SIZE_128;
+		else if (size == 256) alt = OV518_ALT_SIZE_256;
+		else if (size == 384) alt = OV518_ALT_SIZE_384;
+		else if (size == 512) alt = OV518_ALT_SIZE_512;
+		else if (size == 640) alt = OV518_ALT_SIZE_640;
+		else if (size == 768) alt = OV518_ALT_SIZE_768;
+		else if (size == 896) alt = OV518_ALT_SIZE_896;
+		else {
+			err("Set packet size: invalid size (%d)", size);
+			return -EINVAL;
+		}
 	} else {
 		err("Set packet size: Invalid bridge type");
 		return -EINVAL;
@@ -787,351 +1628,1578 @@
 
 	PDEBUG(3, "set packet size: %d, mult=%d, alt=%d", size, mult, alt);
 
-	if (ov511_reg_write(ov511->dev, OV511_REG_FIFO_PACKET_SIZE, mult) < 0)
-		return -ENOMEM;
+	// FIXME: Don't know how to do this on OV518 yet
+	if (ov511->bridge != BRG_OV518 &&
+	    ov511->bridge != BRG_OV518PLUS) {
+		if (ov511_reg_write(ov511->dev, OV511_REG_FIFO_PACKET_SIZE,
+				    mult) < 0) {
+			return -EIO;
+		}
+	}
 	
 	if (usb_set_interface(ov511->dev, ov511->iface, alt) < 0) {
 		err("Set packet size: set interface error");
 		return -EBUSY;
 	}
 
+	/* Initialize the stream */
+	if (ov511->bridge == BRG_OV518 ||
+	    ov511->bridge == BRG_OV518PLUS)
+		if (ov511_reg_write(ov511->dev, 0x2f, 0x80) < 0)
+			return -EIO;
+
 	// FIXME - Should we only reset the FIFO?
-	if (ov511_reset(ov511->dev, OV511_RESET_NOREGS) < 0)
-		return -ENOMEM;
+	if (ov511_reset(ov511, OV511_RESET_NOREGS) < 0)
+		return -EIO;
 
 	ov511->packet_size = size;
 
-	if (ov511_restart(ov511->dev) < 0)
+	if (ov511_restart(ov511) < 0)
 		return -EIO;
 
 	return 0;
 }
 
+/* Upload compression params and quantization tables. Returns 0 for success. */
+static int
+ov511_init_compression(struct usb_ov511 *ov511)
+{
+	struct usb_device *dev = ov511->dev;
+	int rc = 0;
+
+	if (!ov511->compress_inited) {
 
-static inline int
-ov7610_set_picture(struct usb_ov511 *ov511, struct video_picture *p)
+		ov511_reg_write(dev, 0x70, phy);
+		ov511_reg_write(dev, 0x71, phuv);
+		ov511_reg_write(dev, 0x72, pvy);
+		ov511_reg_write(dev, 0x73, pvuv);
+		ov511_reg_write(dev, 0x74, qhy);
+		ov511_reg_write(dev, 0x75, qhuv);
+		ov511_reg_write(dev, 0x76, qvy);
+		ov511_reg_write(dev, 0x77, qvuv);
+
+		if (ov511_upload_quan_tables(dev) < 0) {
+			err("Error uploading quantization tables");
+			rc = -EIO;
+			goto out;
+		}
+	}
+
+	ov511->compress_inited = 1;
+out:	
+	return rc;
+}
+
+/* Upload compression params and quantization tables. Returns 0 for success. */
+static int
+ov518_init_compression(struct usb_ov511 *ov511)
 {
-	int ret;
 	struct usb_device *dev = ov511->dev;
+	int rc = 0;
 
-	PDEBUG(4, "ov511_set_picture");
+	if (!ov511->compress_inited) {
 
-	if (ov511_stop(dev) < 0)
-		return -EIO;
+		if (ov518_upload_quan_tables(dev) < 0) {
+			err("Error uploading quantization tables");
+			rc = -EIO;
+			goto out;
+		}
+	}
 
-	ov511->contrast = p->contrast;
-	ov511->brightness = p->brightness;
-	ov511->colour = p->colour;
-	ov511->hue = p->hue;
-	ov511->whiteness = p->whiteness;
+	ov511->compress_inited = 1;
+out:	
+	return rc;
+}
 
-	if ((ret = ov511_i2c_read(dev, OV7610_REG_COM_B)) < 0)
-		return -EIO;
-#if 0
-	/* disable auto adjust mode */
-	if (ov511_i2c_write(dev, OV7610_REG_COM_B, ret & 0xfe) < 0)
-		return -EIO;
-#endif
-	if (ov511->sensor == SEN_OV7610 || ov511->sensor == SEN_OV7620AE
-		|| ov511->sensor == SEN_OV6620)
-		if (ov511_i2c_write(dev, OV7610_REG_SAT, p->colour >> 8) < 0)
-			return -EIO;
+/* -------------------------------------------------------------------------- */
 
-	if (ov511->sensor == SEN_OV7610 || ov511->sensor == SEN_OV6620) {
-		if (ov511_i2c_write(dev, OV7610_REG_CNT, p->contrast >> 8) < 0)
-			return -EIO;
+/* Sets sensor's contrast setting to "val" */
+static int
+sensor_set_contrast(struct usb_ov511 *ov511, unsigned short val)
+{
+	int rc;
 
-		if (ov511_i2c_write(dev, OV7610_REG_RED, 0xFF - (p->hue >> 8)) < 0)
-			return -EIO;
+	PDEBUG(3, "%d", val);
 
-		if (ov511_i2c_write(dev, OV7610_REG_BLUE, p->hue >> 8) < 0)
+	if (ov511->stop_during_set)
+		if (ov511_stop(ov511) < 0)
 			return -EIO;
 
-		if (ov511_i2c_write(dev, OV7610_REG_BRT, p->brightness >> 8) < 0)
-			return -EIO;
-	} else if ((ov511->sensor == SEN_OV7620) 
-	         || (ov511->sensor == SEN_OV7620AE)) {
-#if 0
-		int cur_sat, new_sat, tmp;
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV6620:
+	case SEN_OV6630:
+	{
+		rc = ov51x_i2c_write(ov511, OV7610_REG_CNT, val >> 8);
+		if (rc < 0)
+			goto out;
+		break;
+	}
+	case SEN_OV7620:
+	{
+		unsigned char ctab[] = {
+			0x01, 0x05, 0x09, 0x11, 0x15, 0x35, 0x37, 0x57,
+			0x5b, 0xa5, 0xa7, 0xc7, 0xc9, 0xcf, 0xef, 0xff
+		};
+
+		/* Use Y gamma control instead. Bit 0 enables it. */
+		rc = ov51x_i2c_write(ov511, 0x64, ctab[val>>12]);
+		if (rc < 0)
+			goto out;
+		break;
+	}
+	case SEN_SAA7111A:
+	{
+		rc = ov51x_i2c_write(ov511, 0x0b, val >> 9);
+		if (rc < 0)
+			goto out;
+		break;
+	}
+	default:
+	{
+		PDEBUG(3, "Unsupported with this sensor");
+		rc = -EPERM;
+		goto out;
+	}
+	}
 
-		cur_sat = ov511_i2c_read(dev, OV7610_REG_BLUE);
+	rc = 0;		/* Success */
+	ov511->contrast = val;
+out:
+	if (ov511_restart(ov511) < 0)
+		return -EIO;
 
-		tmp = (p->hue >> 8) - cur_sat;
-		new_sat = (tmp < 0) ? (-tmp) | 0x80 : tmp;
+	return rc;
+}
 
-	        PDEBUG(1, "cur=%d target=%d diff=%d", cur_sat, p->hue >> 8, tmp); 
+/* Gets sensor's contrast setting */
+static int
+sensor_get_contrast(struct usb_ov511 *ov511, unsigned short *val)
+{
+	int rc;
 
-		if (ov511_i2c_write(dev, OV7610_REG_BLUE, new_sat) < 0)
-			return -EIO;
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV6620:
+	case SEN_OV6630:
+		rc = ov51x_i2c_read(ov511, OV7610_REG_CNT);
+		if (rc < 0)
+			return rc;
+		else
+			*val = rc << 8;
+		break;
+	case SEN_OV7620:
+		/* Use Y gamma reg instead. Bit 0 is the enable bit. */
+		rc = ov51x_i2c_read(ov511, 0x64);
+		if (rc < 0)
+			return rc;
+		else
+			*val = (rc & 0xfe) << 8;
+		break;
+	case SEN_SAA7111A:
+		*val = ov511->contrast;
+		break;
+	default:
+		PDEBUG(3, "Unsupported with this sensor");
+		return -EPERM;
+	}
 
-	        // DEBUG_CODE
-	        PDEBUG(1, "hue=%d", ov511_i2c_read(dev, OV7610_REG_BLUE)); 
+	PDEBUG(3, "%d", *val);
+	ov511->contrast = *val;
 
-#endif
+	return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/* Sets sensor's brightness setting to "val" */
+static int
+sensor_set_brightness(struct usb_ov511 *ov511, unsigned short val)
+{
+	int rc;
+
+	PDEBUG(4, "%d", val);
+
+	if (ov511->stop_during_set)
+		if (ov511_stop(ov511) < 0)
+			return -EIO;
+
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV7620AE:
+	case SEN_OV6620:
+	case SEN_OV6630:
+		rc = ov51x_i2c_write(ov511, OV7610_REG_BRT, val >> 8);
+		if (rc < 0)
+			goto out;
+		break;
+	case SEN_OV7620:
+		/* 7620 doesn't like manual changes when in auto mode */
+		if (!ov511->auto_brt) {
+			rc = ov51x_i2c_write(ov511, OV7610_REG_BRT, val >> 8);
+			if (rc < 0)
+				goto out;
+		}
+		break;
+	case SEN_SAA7111A:
+		rc = ov51x_i2c_write(ov511, 0x0a, val >> 8);
+		if (rc < 0)
+			goto out;
+		break;
+	default:
+		PDEBUG(3, "Unsupported with this sensor");
+		rc = -EPERM;
+		goto out;
 	}
 
-	if (ov511_restart(dev) < 0)
+	rc = 0;		/* Success */
+	ov511->brightness = val;
+out:
+	if (ov511_restart(ov511) < 0)
 		return -EIO;
 
+	return rc;
+}
+
+/* Gets sensor's brightness setting */
+static int
+sensor_get_brightness(struct usb_ov511 *ov511, unsigned short *val)
+{
+	int rc;
+
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV7620AE:
+	case SEN_OV7620:
+	case SEN_OV6620:
+	case SEN_OV6630:
+		rc = ov51x_i2c_read(ov511, OV7610_REG_BRT);
+		if (rc < 0)
+			return rc;
+		else
+			*val = rc << 8;
+		break;
+	case SEN_SAA7111A:
+		*val = ov511->brightness;
+		break;
+	default:
+		PDEBUG(3, "Unsupported with this sensor");
+		return -EPERM;
+	}
+
+	PDEBUG(3, "%d", *val);
+	ov511->brightness = *val;
+
 	return 0;
 }
 
-static inline int
-ov7610_get_picture(struct usb_ov511 *ov511, struct video_picture *p)
+/* -------------------------------------------------------------------------- */
+
+/* Sets sensor's saturation (color intensity) setting to "val" */
+static int
+sensor_set_saturation(struct usb_ov511 *ov511, unsigned short val)
 {
-	int ret;
-	struct usb_device *dev = ov511->dev;
+	int rc;
 
-	PDEBUG(4, "ov511_get_picture");
+	PDEBUG(3, "%d", val);
+
+	if (ov511->stop_during_set)
+		if (ov511_stop(ov511) < 0)
+			return -EIO;
+
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV7620AE:
+	case SEN_OV6620:
+	case SEN_OV6630:
+		rc = ov51x_i2c_write(ov511, OV7610_REG_SAT, val >> 8);
+		if (rc < 0)
+			goto out;
+		break;
+	case SEN_OV7620:
+//		/* Use UV gamma control instead. Bits 0 & 7 are reserved. */
+//		rc = ov511_i2c_write(ov511->dev, 0x62, (val >> 9) & 0x7e);
+//		if (rc < 0)
+//			goto out;
+		rc = ov51x_i2c_write(ov511, OV7610_REG_SAT, val >> 8);
+		if (rc < 0)
+			goto out;
+		break;
+	case SEN_SAA7111A:
+		rc = ov51x_i2c_write(ov511, 0x0c, val >> 9);
+		if (rc < 0)
+			goto out;
+		break;
+	default:
+		PDEBUG(3, "Unsupported with this sensor");
+		rc = -EPERM;
+		goto out;
+	}
 
-	if (ov511_stop(dev) < 0)
+	rc = 0;		/* Success */
+	ov511->colour = val;
+out:
+	if (ov511_restart(ov511) < 0)
 		return -EIO;
 
-	if ((ret = ov511_i2c_read(dev, OV7610_REG_SAT)) < 0) return -EIO;
-	p->colour = ret << 8;
+	return rc;
+}
+
+/* Gets sensor's saturation (color intensity) setting */
+static int
+sensor_get_saturation(struct usb_ov511 *ov511, unsigned short *val)
+{
+	int rc;
+
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV7620AE:
+	case SEN_OV6620:
+	case SEN_OV6630:
+		rc = ov51x_i2c_read(ov511, OV7610_REG_SAT);
+		if (rc < 0)
+			return rc;
+		else
+			*val = rc << 8;
+		break;
+	case SEN_OV7620:
+//		/* Use UV gamma reg instead. Bits 0 & 7 are reserved. */
+//		rc = ov51x_i2c_read(ov511, 0x62);
+//		if (rc < 0)
+//			return rc;
+//		else
+//			*val = (rc & 0x7e) << 9;
+		rc = ov51x_i2c_read(ov511, OV7610_REG_SAT);
+		if (rc < 0)
+			return rc;
+		else
+			*val = rc << 8;
+		break;
+	case SEN_SAA7111A:
+		*val = ov511->colour;
+		break;
+	default:
+		PDEBUG(3, "Unsupported with this sensor");
+		return -EPERM;
+	}
+
+	PDEBUG(3, "%d", *val);
+	ov511->colour = *val;
 
-	if ((ret = ov511_i2c_read(dev, OV7610_REG_CNT)) < 0) return -EIO;
-	p->contrast = ret << 8;
+	return 0;
+}
 
-	if ((ret = ov511_i2c_read(dev, OV7610_REG_BRT)) < 0) return -EIO;
-	p->brightness = ret << 8;
+/* -------------------------------------------------------------------------- */
 
-	/* This may not be the best way to do it */
-	if ((ret = ov511_i2c_read(dev, OV7610_REG_BLUE)) < 0) return -EIO;
-	p->hue = ret << 8;
+/* Sets sensor's hue (red/blue balance) setting to "val" */
+static int
+sensor_set_hue(struct usb_ov511 *ov511, unsigned short val)
+{
+	int rc;
 
-	p->whiteness = 105 << 8;
+	PDEBUG(3, "%d", val);
 
-	/* Can we get these from frame[0]? -claudio? */
-	p->depth = ov511->frame[0].depth;
-	p->palette = ov511->frame[0].format;
+	if (ov511->stop_during_set)
+		if (ov511_stop(ov511) < 0)
+			return -EIO;
+
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV6620:
+	case SEN_OV6630:
+		rc = ov51x_i2c_write(ov511, OV7610_REG_RED, 0xFF - (val >> 8));
+		if (rc < 0)
+			goto out;
+
+		rc = ov51x_i2c_write(ov511, OV7610_REG_BLUE, val >> 8);
+		if (rc < 0)
+			goto out;
+		break;
+	case SEN_OV7620:
+// Hue control is causing problems. I will enable it once it's fixed.
+#if 0
+		rc = ov51x_i2c_write(ov511, 0x7a,
+				     (unsigned char)(val >> 8) + 0xb);
+		if (rc < 0)
+			goto out;
+
+		rc = ov51x_i2c_write(ov511, 0x79, 
+				     (unsigned char)(val >> 8) + 0xb);
+		if (rc < 0)
+			goto out;
+#endif
+		break;
+	case SEN_SAA7111A:
+		rc = ov51x_i2c_write(ov511, 0x0d, (val + 32768) >> 8);
+		if (rc < 0)
+			goto out;
+		break;
+	default:
+		PDEBUG(3, "Unsupported with this sensor");
+		rc = -EPERM;
+		goto out;
+	}
 
-	if (ov511_restart(dev) < 0)
+	rc = 0;		/* Success */
+	ov511->hue = val;
+out:
+	if (ov511_restart(ov511) < 0)
 		return -EIO;
 
+	return rc;
+}
+
+/* Gets sensor's hue (red/blue balance) setting */
+static int
+sensor_get_hue(struct usb_ov511 *ov511, unsigned short *val)
+{
+	int rc;
+
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV6620:
+	case SEN_OV6630:
+		rc = ov51x_i2c_read(ov511, OV7610_REG_BLUE);
+		if (rc < 0)
+			return rc;
+		else
+			*val = rc << 8;
+		break;
+	case SEN_OV7620:
+		rc = ov51x_i2c_read(ov511, 0x7a);
+		if (rc < 0)
+			return rc;
+		else
+			*val = rc << 8;
+		break;
+	case SEN_SAA7111A:
+		*val = ov511->hue;
+		break;
+	default:
+		PDEBUG(3, "Unsupported with this sensor");
+		return -EPERM;
+	}
+
+	PDEBUG(3, "%d", *val);
+	ov511->hue = *val;
+
 	return 0;
 }
 
-/* Returns number of bits per pixel (regardless of where they are located; planar or
- * not), or zero for unsupported format.
- */
-static int ov511_get_depth(int palette)
+/* -------------------------------------------------------------------------- */
+
+static inline int
+sensor_set_picture(struct usb_ov511 *ov511, struct video_picture *p)
 {
-	switch (palette) {
-	case VIDEO_PALETTE_GREY:    return 8;
-	case VIDEO_PALETTE_RGB565:  return 16;
-	case VIDEO_PALETTE_RGB24:   return 24;  
-	case VIDEO_PALETTE_YUV422:  return 16;
-	case VIDEO_PALETTE_YUYV:    return 16;
-	case VIDEO_PALETTE_YUV420:  return 24;
-	case VIDEO_PALETTE_YUV422P: return 24; /* Planar */
-	default:		    return 0;  /* Invalid format */
+	int rc;
+
+	PDEBUG(4, "sensor_set_picture");
+
+	ov511->whiteness = p->whiteness;
+
+	/* Don't return error if a setting is unsupported, or rest of settings
+         * will not be performed */
+
+	rc = sensor_set_contrast(ov511, p->contrast);
+	if (FATAL_ERROR(rc))
+		return rc;
+
+	rc = sensor_set_brightness(ov511, p->brightness);
+	if (FATAL_ERROR(rc))
+		return rc;
+
+	rc = sensor_set_saturation(ov511, p->colour);
+	if (FATAL_ERROR(rc))
+		return rc;
+
+	rc = sensor_set_hue(ov511, p->hue);
+	if (FATAL_ERROR(rc))
+		return rc;
+
+	return 0;
+}
+
+static inline int
+sensor_get_picture(struct usb_ov511 *ov511, struct video_picture *p)
+{
+	int rc;
+
+	PDEBUG(4, "sensor_get_picture");
+
+	/* Don't return error if a setting is unsupported, or rest of settings
+         * will not be performed */
+
+	rc = sensor_get_contrast(ov511, &(p->contrast));
+	if (FATAL_ERROR(rc))
+		return rc;
+
+	rc = sensor_get_brightness(ov511, &(p->brightness));
+	if (FATAL_ERROR(rc))
+		return rc;
+
+	rc = sensor_get_saturation(ov511, &(p->colour));
+	if (FATAL_ERROR(rc))
+		return rc;
+
+	rc = sensor_get_hue(ov511, &(p->hue));
+	if (FATAL_ERROR(rc))
+		return rc;
+
+	p->whiteness = 105 << 8;
+
+	/* Can we get these from frame[0]? -claudio? */
+	p->depth = ov511->frame[0].depth;
+	p->palette = ov511->frame[0].format;
+
+	return 0;
+}
+
+// FIXME: Exposure range is only 0x00-0x7f in interlace mode
+/* Sets current exposure for sensor. This only has an effect if auto-exposure
+ * is off */
+static inline int
+sensor_set_exposure(struct usb_ov511 *ov511, unsigned char val)
+{
+	int rc;
+
+	PDEBUG(3, "%d", val);
+
+	if (ov511->stop_during_set)
+		if (ov511_stop(ov511) < 0)
+			return -EIO;
+
+	switch (ov511->sensor) {
+	case SEN_OV6620:
+	case SEN_OV6630:
+	case SEN_OV7610:
+	case SEN_OV7620:
+	case SEN_OV7620AE:
+	case SEN_OV8600:
+		rc = ov51x_i2c_write(ov511, 0x10, val);
+		if (rc < 0)
+			goto out;
+
+		break;
+	case SEN_KS0127:
+	case SEN_KS0127B:
+	case SEN_SAA7111A:
+		PDEBUG(3, "Unsupported with this sensor");
+		return -EPERM;
+	default:
+		err("Sensor not supported for set_exposure");
+		return -EINVAL;
+	}
+
+	rc = 0;		/* Success */
+	ov511->exposure = val;
+out:
+	if (ov511_restart(ov511) < 0)
+		return -EIO;
+
+	return rc;
+}
+
+/* Gets current exposure level from sensor, regardless of whether it is under
+ * manual control. */
+static int
+sensor_get_exposure(struct usb_ov511 *ov511, unsigned char *val)
+{
+	int rc;
+
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV6620:
+	case SEN_OV6630:
+	case SEN_OV7620:
+	case SEN_OV7620AE:
+	case SEN_OV8600:
+		rc = ov51x_i2c_read(ov511, 0x10);
+		if (rc < 0)
+			return rc;
+		else
+			*val = rc;
+		break;
+	case SEN_KS0127:
+	case SEN_KS0127B:
+	case SEN_SAA7111A:
+		val = 0;
+		PDEBUG(3, "Unsupported with this sensor");
+		return -EPERM;
+	default:
+		err("Sensor not supported for get_exposure");
+		return -EINVAL;
+	}
+
+	PDEBUG(3, "%d", *val);
+	ov511->exposure = *val;
+
+	return 0;
+}
+
+/* Turns on or off the LED. Only has an effect with OV511+/OV518(+) */
+static inline void 
+ov51x_led_control(struct usb_ov511 *ov511, int enable)
+{
+	PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
+
+	if (ov511->bridge == BRG_OV511PLUS)
+		ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_LED_CTL, 
+			        enable ? 1 : 0);
+	else if (ov511->bridge == BRG_OV518 ||
+		 ov511->bridge == BRG_OV518PLUS)
+		ov511_reg_write_mask(ov511->dev, OV518_REG_GPIO_OUT, 
+				     enable ? 0x02 : 0x00, 0x02);
+	return;
+}
+
+/* Matches the sensor's internal frame rate to the lighting frequency.
+ * Valid frequencies are:
+ *	50 - 50Hz, for European and Asian lighting
+ *	60 - 60Hz, for American lighting
+ *
+ * Tested with: OV7610, OV7620, OV7620AE, OV6620
+ * Unsupported: KS0127, KS0127B, SAA7111A
+ * Returns: 0 for success
+ */
+static int
+sensor_set_light_freq(struct usb_ov511 *ov511, int freq)
+{
+	int sixty;
+
+	PDEBUG(4, "%d Hz", freq);
+
+	if (freq == 60)
+		sixty = 1;
+	else if (freq == 50)
+		sixty = 0;
+	else {
+		err("Invalid light freq (%d Hz)", freq);
+		return -EINVAL;
+	}
+
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+		ov51x_i2c_write_mask(ov511, 0x2a, sixty?0x00:0x80, 0x80);
+		ov51x_i2c_write(ov511, 0x2b, sixty?0x00:0xac);
+		ov51x_i2c_write_mask(ov511, 0x13, 0x10, 0x10);
+		ov51x_i2c_write_mask(ov511, 0x13, 0x00, 0x10);
+		break;
+	case SEN_OV7620:
+	case SEN_OV7620AE:
+	case SEN_OV8600:
+		ov51x_i2c_write_mask(ov511, 0x2a, sixty?0x00:0x80, 0x80);
+		ov51x_i2c_write(ov511, 0x2b, sixty?0x00:0xac);
+		ov51x_i2c_write_mask(ov511, 0x76, 0x01, 0x01);
+		break;		
+	case SEN_OV6620:
+	case SEN_OV6630:
+		ov51x_i2c_write(ov511, 0x2b, sixty?0xa8:0x28);
+		ov51x_i2c_write(ov511, 0x2a, sixty?0x84:0xa4);
+		break;
+	case SEN_KS0127:
+	case SEN_KS0127B:
+	case SEN_SAA7111A:
+		PDEBUG(5, "Unsupported with this sensor");
+		return -EPERM;
+	default:
+		err("Sensor not supported for set_light_freq");
+		return -EINVAL;
+	}
+
+	ov511->lightfreq = freq;
+
+	return 0;
+}
+
+/* If enable is true, turn on the sensor's banding filter, otherwise turn it
+ * off. This filter tries to reduce the pattern of horizontal light/dark bands
+ * caused by some (usually fluorescent) lighting. The light frequency must be
+ * set either before or after enabling it with ov51x_set_light_freq().
+ *
+ * Tested with: OV7610, OV7620, OV7620AE, OV6620.
+ * Unsupported: KS0127, KS0127B, SAA7111A
+ * Returns: 0 for success
+ */
+static inline int
+sensor_set_banding_filter(struct usb_ov511 *ov511, int enable)
+{
+	int rc;
+
+	PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
+
+	if (ov511->sensor == SEN_KS0127 || ov511->sensor == SEN_KS0127B
+		|| ov511->sensor == SEN_SAA7111A) {
+		PDEBUG(5, "Unsupported with this sensor");
+		return -EPERM;
+	}
+
+	rc = ov51x_i2c_write_mask(ov511, 0x2d, enable?0x04:0x00, 0x04);
+	if (rc < 0)
+		return rc;
+
+	ov511->bandfilt = enable;
+
+	return 0;
+}
+
+/* If enable is true, turn on the sensor's auto brightness control, otherwise
+ * turn it off.
+ *
+ * Unsupported: KS0127, KS0127B, SAA7111A
+ * Returns: 0 for success
+ */
+static inline int
+sensor_set_auto_brightness(struct usb_ov511 *ov511, int enable)
+{
+	int rc;
+
+	PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
+
+	if (ov511->sensor == SEN_KS0127 || ov511->sensor == SEN_KS0127B
+		|| ov511->sensor == SEN_SAA7111A) {
+		PDEBUG(5, "Unsupported with this sensor");
+		return -EPERM;
+	}
+
+	rc = ov51x_i2c_write_mask(ov511, 0x2d, enable?0x10:0x00, 0x10);
+	if (rc < 0)
+		return rc;
+
+	ov511->auto_brt = enable;
+
+	return 0;
+}
+
+/* If enable is true, turn on the sensor's auto exposure control, otherwise
+ * turn it off.
+ *
+ * Unsupported: KS0127, KS0127B, SAA7111A
+ * Returns: 0 for success
+ */
+static inline int
+sensor_set_auto_exposure(struct usb_ov511 *ov511, int enable)
+{	
+	PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
+
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+		ov51x_i2c_write_mask(ov511, 0x29, enable?0x00:0x80, 0x80);
+		break;
+	case SEN_OV6620:
+	case SEN_OV7620:
+	case SEN_OV7620AE:
+	case SEN_OV8600:
+		ov51x_i2c_write_mask(ov511, 0x13, enable?0x01:0x00, 0x01);
+		break;		
+	case SEN_OV6630:
+		ov51x_i2c_write_mask(ov511, 0x28, enable?0x00:0x10, 0x10);
+		break;
+	case SEN_KS0127:
+	case SEN_KS0127B:
+	case SEN_SAA7111A:
+		PDEBUG(5, "Unsupported with this sensor");
+		return -EPERM;
+	default:
+		err("Sensor not supported for set_auto_exposure");
+		return -EINVAL;
+	}
+
+	ov511->auto_exp = enable;
+
+	return 0;
+}
+
+/* Modifies the sensor's exposure algorithm to allow proper exposure of objects
+ * that are illuminated from behind.
+ *
+ * Tested with: OV6620, OV7620
+ * Unsupported: OV7610, OV7620AE, KS0127, KS0127B, SAA7111A
+ * Returns: 0 for success
+ */
+static int
+sensor_set_backlight(struct usb_ov511 *ov511, int enable)
+{
+
+	PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
+
+	switch (ov511->sensor) {
+	case SEN_OV7620:
+	case SEN_OV8600:
+		ov51x_i2c_write_mask(ov511, 0x68, enable?0xe0:0xc0, 0xe0);
+		ov51x_i2c_write_mask(ov511, 0x29, enable?0x08:0x00, 0x08);
+		ov51x_i2c_write_mask(ov511, 0x28, enable?0x02:0x00, 0x02);
+		break;		
+	case SEN_OV6620:
+		ov51x_i2c_write_mask(ov511, 0x4e, enable?0xe0:0xc0, 0xe0);
+		ov51x_i2c_write_mask(ov511, 0x29, enable?0x08:0x00, 0x08);
+		ov51x_i2c_write_mask(ov511, 0x0e, enable?0x80:0x00, 0x80);
+		break;
+	case SEN_OV6630:
+		ov51x_i2c_write_mask(ov511, 0x4e, enable?0x80:0x60, 0xe0);
+		ov51x_i2c_write_mask(ov511, 0x29, enable?0x08:0x00, 0x08);
+		ov51x_i2c_write_mask(ov511, 0x28, enable?0x02:0x00, 0x02);
+		break;
+	case SEN_OV7610:
+	case SEN_OV7620AE:
+	case SEN_KS0127:
+	case SEN_KS0127B:
+	case SEN_SAA7111A:
+		PDEBUG(5, "Unsupported with this sensor");
+		return -EPERM;
+	default:
+		err("Sensor not supported for set_backlight");
+		return -EINVAL;
+	}
+
+	ov511->backlight = enable;
+
+	return 0;
+}
+
+/* Returns number of bits per pixel (regardless of where they are located;
+ * planar or not), or zero for unsupported format.
+ */
+static inline int 
+ov511_get_depth(int palette)
+{
+	switch (palette) {
+	case VIDEO_PALETTE_GREY:    return 8;
+	case VIDEO_PALETTE_RGB565:  return 16;
+	case VIDEO_PALETTE_RGB24:   return 24;  
+	case VIDEO_PALETTE_YUV422:  return 16;
+	case VIDEO_PALETTE_YUYV:    return 16;
+	case VIDEO_PALETTE_YUV420:  return 12;
+	case VIDEO_PALETTE_YUV422P: return 16; /* Planar */
+	case VIDEO_PALETTE_YUV420P: return 12; /* Planar */
+	default:		    return 0;  /* Invalid format */
+	}
+}
+
+/* Bytes per frame. Used by read(). Return of 0 indicates error */
+static inline long int 
+get_frame_length(struct ov511_frame *frame)
+{
+	if (!frame)
+		return 0;
+	else
+		return ((frame->width * frame->height
+			 * ov511_get_depth(frame->format)) >> 3);
+}
+
+static int
+mode_init_ov_sensor_regs(struct usb_ov511 *ov511, int width, int height,
+			 int mode, int sub_flag, int qvga)
+{
+	int clock;
+
+	/******** Mode (VGA/QVGA) and sensor specific regs ********/
+
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+		ov51x_i2c_write(ov511, 0x14, qvga?0x24:0x04);
+// FIXME: Does this improve the image quality or frame rate?
+#if 0
+		ov51x_i2c_write_mask(ov511, 0x28, qvga?0x00:0x20, 0x20);
+		ov51x_i2c_write(ov511, 0x24, 0x10);
+		ov51x_i2c_write(ov511, 0x25, qvga?0x40:0x8a);
+		ov51x_i2c_write(ov511, 0x2f, qvga?0x30:0xb0);
+		ov51x_i2c_write(ov511, 0x35, qvga?0x1c:0x9c);
+#endif
+		break;
+	case SEN_OV7620:
+//		ov51x_i2c_write(ov511, 0x2b, 0x00);
+		ov51x_i2c_write(ov511, 0x14, qvga?0xa4:0x84);
+		ov51x_i2c_write_mask(ov511, 0x28, qvga?0x00:0x20, 0x20);
+		ov51x_i2c_write(ov511, 0x24, qvga?0x20:0x3a);
+		ov51x_i2c_write(ov511, 0x25, qvga?0x30:0x60);
+		ov51x_i2c_write_mask(ov511, 0x2d, qvga?0x40:0x00, 0x40);
+		ov51x_i2c_write_mask(ov511, 0x67, qvga?0xf0:0x90, 0xf0);
+		ov51x_i2c_write_mask(ov511, 0x74, qvga?0x20:0x00, 0x20);
+		break;
+	case SEN_OV7620AE:
+//		ov51x_i2c_write(ov511, 0x2b, 0x00);
+		ov51x_i2c_write(ov511, 0x14, qvga?0xa4:0x84);
+// FIXME: Enable this once 7620AE uses 7620 initial settings
+#if 0
+		ov51x_i2c_write_mask(ov511, 0x28, qvga?0x00:0x20, 0x20);
+		ov51x_i2c_write(ov511, 0x24, qvga?0x20:0x3a);
+		ov51x_i2c_write(ov511, 0x25, qvga?0x30:0x60);
+		ov51x_i2c_write_mask(ov511, 0x2d, qvga?0x40:0x00, 0x40);
+		ov51x_i2c_write_mask(ov511, 0x67, qvga?0xb0:0x90, 0xf0);
+		ov51x_i2c_write_mask(ov511, 0x74, qvga?0x20:0x00, 0x20);
+#endif
+		break;
+	case SEN_OV6620:
+	case SEN_OV6630:
+		ov51x_i2c_write(ov511, 0x14, qvga?0x24:0x04);
+		/* No special settings yet */
+		break;
+	default:
+		err("Invalid sensor");
+		return -EINVAL;
+	}
+
+	/******** Palette-specific regs ********/
+
+	if (mode == VIDEO_PALETTE_GREY) {
+		if (ov511->sensor == SEN_OV7610
+		    || ov511->sensor == SEN_OV7620AE) {
+			/* these aren't valid on the OV6620/OV7620/6630? */
+			ov51x_i2c_write_mask(ov511, 0x0e, 0x40, 0x40);
+		}
+		ov51x_i2c_write_mask(ov511, 0x13, 0x20, 0x20);
+	} else {
+		if (ov511->sensor == SEN_OV7610
+		    || ov511->sensor == SEN_OV7620AE) {
+			/* not valid on the OV6620/OV7620/6630? */
+			ov51x_i2c_write_mask(ov511, 0x0e, 0x00, 0x40);
+		}
+		ov51x_i2c_write_mask(ov511, 0x13, 0x00, 0x20);
+	}
+
+	/******** Clock programming ********/
+
+	// FIXME: Test this with OV6630
+
+	/* The OV6620 needs special handling. This prevents the 
+	 * severe banding that normally occurs */
+	if (ov511->sensor == SEN_OV6620 || ov511->sensor == SEN_OV6630)
+	{
+		/* Clock down */
+
+		ov51x_i2c_write(ov511, 0x2a, 0x04);
+
+		if (ov511->compress) {
+//			clock = 0;    /* This ensures the highest frame rate */
+			clock = 3;
+		} else if (clockdiv == -1) {   /* If user didn't override it */
+			clock = 3;    /* Gives better exposure time */
+		} else {
+			clock = clockdiv;
+		}
+
+		PDEBUG(4, "Setting clock divisor to %d", clock);
+
+		ov51x_i2c_write(ov511, 0x11, clock);
+
+		ov51x_i2c_write(ov511, 0x2a, 0x84);
+		/* This next setting is critical. It seems to improve
+		 * the gain or the contrast. The "reserved" bits seem
+		 * to have some effect in this case. */
+		ov51x_i2c_write(ov511, 0x2d, 0x85);
+	}
+	else
+	{
+		if (ov511->compress) {
+			clock = 1;    /* This ensures the highest frame rate */
+		} else if (clockdiv == -1) {   /* If user didn't override it */
+			/* Calculate and set the clock divisor */
+			clock = ((sub_flag ? ov511->subw * ov511->subh
+				  : width * height)
+				 * (mode == VIDEO_PALETTE_GREY ? 2 : 3) / 2)
+				 / 66000;
+		} else {
+			clock = clockdiv;
+		}
+
+		PDEBUG(4, "Setting clock divisor to %d", clock);
+
+		ov51x_i2c_write(ov511, 0x11, clock);
+	}
+
+	/******** Special Features ********/
+
+	if (framedrop >= 0)
+		ov51x_i2c_write(ov511, 0x16, framedrop);
+
+	/* We only have code to convert GBR -> RGB24 */
+	if ((mode == VIDEO_PALETTE_RGB24) && sensor_gbr)
+		ov51x_i2c_write_mask(ov511, 0x12, 0x08, 0x08);
+	else
+		ov51x_i2c_write_mask(ov511, 0x12, 0x00, 0x08);
+
+	/* Test Pattern */
+	ov51x_i2c_write_mask(ov511, 0x12, (testpat?0x02:0x00), 0x02);
+
+	/* Auto white balance */
+//	if (awb)
+		ov51x_i2c_write_mask(ov511, 0x12, 0x04, 0x04);
+//	else
+//		ov51x_i2c_write_mask(ov511, 0x12, 0x00, 0x04);
+
+	// This will go away as soon as ov511_mode_init_sensor_regs()
+	// is fully tested.
+	/* 7620/6620/6630? don't have register 0x35, so play it safe */
+	if (ov511->sensor == SEN_OV7610 ||
+	    ov511->sensor == SEN_OV7620AE) {
+		if (width == 640 && height == 480)
+			ov51x_i2c_write(ov511, 0x35, 0x9e);
+		else
+			ov51x_i2c_write(ov511, 0x35, 0x1e);
+	}
+
+	return 0;
+}
+
+static int
+set_ov_sensor_window(struct usb_ov511 *ov511, int width, int height, int mode,
+		     int sub_flag)
+{
+	int ret;
+	int hwsbase, hwebase, vwsbase, vwebase, hwsize, vwsize; 
+	int hoffset, voffset, hwscale = 0, vwscale = 0;
+
+	/* The different sensor ICs handle setting up of window differently.
+	 * IF YOU SET IT WRONG, YOU WILL GET ALL ZERO ISOC DATA FROM OV51x!!! */
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV7620AE:
+		hwsbase = 0x38;
+		hwebase = 0x3a;
+		vwsbase = vwebase = 0x05;
+		break;
+	case SEN_OV6620:
+	case SEN_OV6630:	// FIXME: Is this right?
+		hwsbase = 0x38;
+		hwebase = 0x3a;
+		vwsbase = 0x05;
+		vwebase = 0x06;
+		break;
+	case SEN_OV7620:
+		hwsbase = 0x2f;		/* From 7620.SET (spec is wrong) */
+		hwebase = 0x2f;
+		vwsbase = vwebase = 0x05;
+		break;
+	default:
+		err("Invalid sensor");
+		return -EINVAL;
+	}
+
+	if (ov511->sensor == SEN_OV6620 || ov511->sensor == SEN_OV6630) {
+		if (width > 176 && height > 144) {  /* CIF */
+			ret = mode_init_ov_sensor_regs(ov511, width, height,
+				mode, sub_flag, 0);
+			if (ret < 0)
+				return ret;
+			hwscale = 1;
+			vwscale = 1;  /* The datasheet says 0; it's wrong */
+			hwsize = 352;
+			vwsize = 288;
+		} else if (width > 176 || height > 144) {
+			err("Illegal dimensions");
+			return -EINVAL;
+		} else {			    /* QCIF */
+			ret = mode_init_ov_sensor_regs(ov511, width, height,
+				mode, sub_flag, 1);
+			if (ret < 0)
+				return ret;
+			hwsize = 176;
+			vwsize = 144;
+		}
+	} else {
+		if (width > 320 && height > 240) {  /* VGA */
+			ret = mode_init_ov_sensor_regs(ov511, width, height,
+				mode, sub_flag, 0);
+			if (ret < 0)
+				return ret;
+			hwscale = 2;
+			vwscale = 1;
+			hwsize = 640;
+			vwsize = 480;
+		} else if (width > 320 || height > 240) {
+			err("Illegal dimensions");
+			return -EINVAL;
+		} else {			    /* QVGA */
+			ret = mode_init_ov_sensor_regs(ov511, width, height,
+				mode, sub_flag, 1);
+			if (ret < 0)
+				return ret;
+			hwscale = 1;
+			hwsize = 320;
+			vwsize = 240;
+		}
+	}
+
+	/* Center the window */
+	hoffset = ((hwsize - width) / 2) >> hwscale;
+	voffset = ((vwsize - height) / 2) >> vwscale;
+
+	/* FIXME! - This needs to be changed to support 160x120 and 6620!!! */
+	if (sub_flag) {
+		ov51x_i2c_write(ov511, 0x17, hwsbase+(ov511->subx>>hwscale));
+		ov51x_i2c_write(ov511, 0x18,
+				hwebase+((ov511->subx+ov511->subw)>>hwscale));
+		ov51x_i2c_write(ov511, 0x19, vwsbase+(ov511->suby>>vwscale));
+		ov51x_i2c_write(ov511, 0x1a,
+				vwebase+((ov511->suby+ov511->subh)>>vwscale));
+	} else {
+		ov51x_i2c_write(ov511, 0x17, hwsbase + hoffset);
+		ov51x_i2c_write(ov511, 0x18,
+				hwebase + hoffset + (hwsize>>hwscale));
+		ov51x_i2c_write(ov511, 0x19, vwsbase + voffset);
+		ov51x_i2c_write(ov511, 0x1a,
+				vwebase + voffset + (vwsize>>vwscale));
+	}
+
+#ifdef OV511_DEBUG
+	if (dump_sensor)
+		ov51x_dump_i2c_regs(ov511);
+#endif
+
+	return 0;
+}
+
+/* Set up the OV511/OV511+ with the given image parameters.
+ *
+ * Do not put any sensor-specific code in here (including I2C I/O functions)
+ */
+static int
+ov511_mode_init_regs(struct usb_ov511 *ov511,
+		     int width, int height, int mode, int sub_flag)
+{
+	int lncnt, pxcnt, rc = 0;
+	struct usb_device *dev = ov511->dev;
+
+	if (!ov511 || !dev)
+		return -EFAULT;
+
+	if (sub_flag) {
+		width = ov511->subw;
+		height = ov511->subh;
+	}
+
+	PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d",
+	       width, height, mode, sub_flag);
+
+	// FIXME: This should be moved to a 7111a-specific function once
+	// subcapture is dealt with properly
+	if (ov511->sensor == SEN_SAA7111A) {
+		if (width == 320 && height == 240) {
+			/* No need to do anything special */
+		} else if (width == 640 && height == 480) {
+			/* Set the OV511 up as 320x480, but keep the V4L
+			 * resolution as 640x480 */
+			width = 320;
+		} else {
+			err("SAA7111A only supports 320x240 or 640x480");
+			return -EINVAL;
+		}
+	}
+
+	/* Make sure width and height are a multiple of 8 */
+	if (width % 8 || height % 8) {
+		err("Invalid size (%d, %d) (mode = %d)", width, height, mode);
+		return -EINVAL;
+	}
+
+	if (width < ov511->minwidth || height < ov511->minheight) {
+		err("Requested dimensions are too small");
+		return -EINVAL;
+	}
+
+	if (ov511_stop(ov511) < 0)
+		return -EIO;
+
+	if (mode == VIDEO_PALETTE_GREY) {
+		ov511_reg_write(dev, 0x16, 0x00);
+
+		/* For snapshot */
+		ov511_reg_write(dev, 0x1e, 0x00);
+		ov511_reg_write(dev, 0x1f, 0x01);
+	} else {
+		ov511_reg_write(dev, 0x16, 0x01);
+
+		/* For snapshot */
+		ov511_reg_write(dev, 0x1e, 0x01);
+		ov511_reg_write(dev, 0x1f, 0x03);
+	}
+
+	/* Here I'm assuming that snapshot size == image size.
+	 * I hope that's always true. --claudio
+	 */
+	pxcnt = (width >> 3) - 1;
+	lncnt = (height >> 3) - 1;
+
+	ov511_reg_write(dev, 0x12, pxcnt);
+	ov511_reg_write(dev, 0x13, lncnt);
+	ov511_reg_write(dev, 0x14, 0x00);
+	ov511_reg_write(dev, 0x15, 0x00);
+	ov511_reg_write(dev, 0x18, 0x03);	/* YUV420, low pass filer on */
+
+	/* Snapshot additions */
+	ov511_reg_write(dev, 0x1a, pxcnt);
+	ov511_reg_write(dev, 0x1b, lncnt);
+        ov511_reg_write(dev, 0x1c, 0x00);
+        ov511_reg_write(dev, 0x1d, 0x00);
+
+	if (ov511->compress) {
+		ov511_reg_write(dev, 0x78, 0x07); // Turn on Y & UV compression
+		ov511_reg_write(dev, 0x79, 0x03); // Enable LUTs
+		ov511_reset(ov511, OV511_RESET_OMNICE);
 	}
+//out:
+	if (ov511_restart(ov511) < 0)
+		return -EIO;
+
+	return rc;
 }
 
-/* LNCNT values fixed by Lawrence Glaister <lg@jfm.bc.ca> */
-static struct mode_list mlist[] = {
-	/* W    H   C  PXCNT LNCNT PXDIV LNDIV M420  COMA  COML */
-	{ 640, 480, 0, 0x4f, 0x3b, 0x00, 0x00, 0x03, 0x24, 0x9e },
-	{ 640, 480, 1, 0x4f, 0x3b, 0x00, 0x00, 0x03, 0x24, 0x9e },
-	{ 320, 240, 0, 0x27, 0x1d, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 320, 240, 1, 0x27, 0x1d, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 352, 288, 0, 0x2b, 0x25, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 352, 288, 1, 0x2b, 0x25, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 384, 288, 0, 0x2f, 0x25, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 384, 288, 1, 0x2f, 0x25, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 448, 336, 0, 0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 448, 336, 1, 0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 176, 144, 0, 0x15, 0x12, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 176, 144, 1, 0x15, 0x12, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 160, 120, 0, 0x13, 0x0e, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 160, 120, 1, 0x13, 0x0e, 0x00, 0x00, 0x03, 0x04, 0x1e },
+static struct mode_list_518 mlist518[] = {
+	/* W    H   reg28 reg29 reg2a reg2c reg2e reg24 reg25 */
+	{ 352, 288, 0x00, 0x16, 0x48, 0x00, 0x00, 0x9f, 0x90 },
+	{ 320, 240, 0x00, 0x14, 0x3c, 0x10, 0x18, 0x9f, 0x90 },
+	{ 176, 144, 0x05, 0x0b, 0x24, 0x00, 0x00, 0xff, 0xf0 },
+	{ 160, 120, 0x05, 0x0a, 0x1e, 0x08, 0x0c, 0xff, 0xf0 },
 	{ 0, 0 }
 };
 
+/* Sets up the OV518/OV518+ with the given image parameters
+ *
+ * OV518 needs a completely different approach, until we can figure out what
+ * the individual registers do. Many register ops are commented out until we
+ * can find out if they are still valid. Also, only 15 FPS is supported now.
+ *
+ * Do not put any sensor-specific code in here (including I2C I/O functions)
+ */
 static int
-ov511_mode_init_regs(struct usb_ov511 *ov511,
+ov518_mode_init_regs(struct usb_ov511 *ov511,
 		     int width, int height, int mode, int sub_flag)
 {
 	int i;
 	struct usb_device *dev = ov511->dev;
-	int hwsbase, hwebase, vwsbase, vwebase, hwsize, vwsize; 
-	int hwscale = 0, vwscale = 0;
+	unsigned char b[3]; /* Multiple-value reg buffer */
 
 	PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d",
 	       width, height, mode, sub_flag);
 
-	if (ov511_stop(ov511->dev) < 0)
+	if (ov511_stop(ov511) < 0)
+		return -EIO;
+
+	for (i = 0; mlist518[i].width; i++) {
+//		int lncnt, pxcnt;
+
+		if (width != mlist518[i].width || height != mlist518[i].height)
+			continue;
+
+// FIXME: Subcapture won't be possible until we know what the registers do
+// FIXME: We can't handle anything but YUV420 so far
+
+//		/* Here I'm assuming that snapshot size == image size.
+//		 * I hope that's always true. --claudio
+//		 */
+//		pxcnt = sub_flag ? (ov511->subw >> 3) - 1 : mlist[i].pxcnt;
+//		lncnt = sub_flag ? (ov511->subh >> 3) - 1 : mlist[i].lncnt;
+//
+//		ov511_reg_write(dev, 0x12, pxcnt);
+//		ov511_reg_write(dev, 0x13, lncnt);
+
+		/******** Set the mode ********/		
+
+		/* Mode independent regs */
+		ov511_reg_write(dev, 0x2b, 0x00);
+		ov511_reg_write(dev, 0x2d, 0x00);
+		ov511_reg_write(dev, 0x3b, 0x00);
+		ov511_reg_write(dev, 0x3d, 0x00);
+
+		/* Mode dependent regs. Regs 38 - 3e are always the same as
+		 * regs 28 - 2e */
+		ov511_reg_write_mask(dev, 0x28, mlist518[i].reg28
+			| (mode == VIDEO_PALETTE_GREY) ? 0x80:0x00, 0x8f);
+		ov511_reg_write(dev, 0x29, mlist518[i].reg29);
+		ov511_reg_write(dev, 0x2a, mlist518[i].reg2a);
+		ov511_reg_write(dev, 0x2c, mlist518[i].reg2c);
+		ov511_reg_write(dev, 0x2e, mlist518[i].reg2e);
+		ov511_reg_write_mask(dev, 0x38, mlist518[i].reg28 
+			| (mode == VIDEO_PALETTE_GREY) ? 0x80:0x00, 0x8f);
+		ov511_reg_write(dev, 0x39, mlist518[i].reg29);
+		ov511_reg_write(dev, 0x3a, mlist518[i].reg2a);
+		ov511_reg_write(dev, 0x3c, mlist518[i].reg2c);
+		ov511_reg_write(dev, 0x3e, mlist518[i].reg2e);
+		ov511_reg_write(dev, 0x24, mlist518[i].reg24);
+		ov511_reg_write(dev, 0x25, mlist518[i].reg25);
+
+		/* Windows driver does this here; who knows why */
+		ov511_reg_write(dev, 0x2f, 0x80);
+
+		/******** Set the framerate (to 15 FPS) ********/		
+
+		/* Mode independent, but framerate dependent, regs */
+		/* These are for 15 FPS only */
+		ov511_reg_write(dev, 0x51, 0x08);
+		ov511_reg_write(dev, 0x22, 0x18);
+		ov511_reg_write(dev, 0x23, 0xff);
+		ov511_reg_write(dev, 0x71, 0x19);  /* Compression-related? */
+
+		// FIXME: Sensor-specific
+		/* Bit 5 is what matters here. Of course, it is "reserved" */
+		ov51x_i2c_write(ov511, 0x54, 0x23);
+
+		ov511_reg_write(dev, 0x2f, 0x80);
+
+		/* Mode dependent regs */
+		if ((width == 352 && height == 288) ||
+		    (width == 320 && height == 240)) {
+			b[0]=0x80; b[1]=0x02;
+			ov518_reg_write_multi(dev, 0x30, b, 2);
+			b[0]=0x90; b[1]=0x01;
+			ov518_reg_write_multi(dev, 0xc4, b, 2);
+			b[0]=0xf4; b[1]=0x01;
+			ov518_reg_write_multi(dev, 0xc6, b, 2);
+			b[0]=0xf4; b[1]=0x01;
+			ov518_reg_write_multi(dev, 0xc7, b, 2);
+			b[0]=0x8e; b[1]=0x00;
+			ov518_reg_write_multi(dev, 0xc8, b, 2);
+			b[0]=0x1a; b[1]=0x00; b[2]=0x02;
+			ov518_reg_write_multi(dev, 0xca, b, 3);
+			b[0]=0x14; b[1]=0x02;
+			ov518_reg_write_multi(dev, 0xcb, b, 2);
+			b[0]=0xd0; b[1]=0x07;
+			ov518_reg_write_multi(dev, 0xcc, b, 2);
+			b[0]=0x20; b[1]=0x00;
+			ov518_reg_write_multi(dev, 0xcd, b, 2);
+			b[0]=0x60; b[1]=0x02;
+			ov518_reg_write_multi(dev, 0xce, b, 2);
+
+		} else if ((width == 176 && height == 144) ||
+			   (width == 160 && height == 120)) {
+			b[0]=0x80; b[1]=0x01;
+			ov518_reg_write_multi(dev, 0x30, b, 2);
+			b[0]=0xc8; b[1]=0x00;
+			ov518_reg_write_multi(dev, 0xc4, b, 2);
+			b[0]=0x40; b[1]=0x01;
+			ov518_reg_write_multi(dev, 0xc6, b, 2);
+			b[0]=0x40; b[1]=0x01;
+			ov518_reg_write_multi(dev, 0xc7, b, 2);
+			b[0]=0x60; b[1]=0x00;
+			ov518_reg_write_multi(dev, 0xc8, b, 2);
+			b[0]=0x0f; b[1]=0x33; b[2]=0x01;
+			ov518_reg_write_multi(dev, 0xca, b, 3);
+			b[0]=0x40; b[1]=0x01;
+			ov518_reg_write_multi(dev, 0xcb, b, 2);
+			b[0]=0xec; b[1]=0x04;
+			ov518_reg_write_multi(dev, 0xcc, b, 2);
+			b[0]=0x13; b[1]=0x00;
+			ov518_reg_write_multi(dev, 0xcd, b, 2);
+			b[0]=0x6d; b[1]=0x01;
+			ov518_reg_write_multi(dev, 0xce, b, 2);
+		} else {
+			/* Can't happen, since we already handled this case */
+			err("ov518_mode_init_regs(): **** logic error ****");
+		}
+
+		ov511_reg_write(dev, 0x2f, 0x80);
+
+		break;
+	}
+
+	if (ov511_restart(ov511) < 0)
+		return -EIO;
+
+	/* Reset it just for good measure */
+	if (ov511_reset(ov511, OV511_RESET_NOREGS) < 0)
 		return -EIO;
 
-	/* Dumppix only works with RGB24 */
-	if (dumppix && (mode != VIDEO_PALETTE_RGB24)) {
-		err("dumppix only supported with RGB 24");
+	if (mlist518[i].width == 0) {
+		err("Unknown mode (%d, %d): %d", width, height, mode);
 		return -EINVAL;
 	}
 
-	if (mode == VIDEO_PALETTE_GREY) {
-		ov511_reg_write(dev, 0x16, 0x00);
-		if (ov511->sensor == SEN_OV7610
-		    || ov511->sensor == SEN_OV7620AE) {
-			/* these aren't valid on the OV6620/OV7620 */
-			ov511_i2c_write(dev, 0x0e, 0x44);
-		}
-		ov511_i2c_write(dev, 0x13, autoadjust ? 0x21 : 0x20);
+	return 0;
+}
 
-		/* For snapshot */
-		ov511_reg_write(dev, 0x1e, 0x00);
-		ov511_reg_write(dev, 0x1f, 0x01);
-	} else {
-		ov511_reg_write(dev, 0x16, 0x01);
-		if (ov511->sensor == SEN_OV7610
-		    || ov511->sensor == SEN_OV7620AE) {
-			/* not valid on the OV6620/OV7620 */
-			ov511_i2c_write(dev, 0x0e, 0x04);
-		}
-		ov511_i2c_write(dev, 0x13, autoadjust ? 0x01 : 0x00);
+/* This is a wrapper around the OV511, OV518, and sensor specific functions */
+static int
+mode_init_regs(struct usb_ov511 *ov511,
+	       int width, int height, int mode, int sub_flag)
+{
+	int rc = 0;
 
-		/* For snapshot */
-		ov511_reg_write(dev, 0x1e, 0x01);
-		ov511_reg_write(dev, 0x1f, 0x03);
+	if (ov511->bridge == BRG_OV518 ||
+	    ov511->bridge == BRG_OV518PLUS) {
+		rc = ov518_mode_init_regs(ov511, width, height, mode, sub_flag);
+	} else {
+		rc = ov511_mode_init_regs(ov511, width, height, mode, sub_flag);
 	}
 
-	/* The different sensor ICs handle setting up of window differently */
+	if (FATAL_ERROR(rc))
+		return rc;
+
 	switch (ov511->sensor) {
 	case SEN_OV7610:
+	case SEN_OV7620:
 	case SEN_OV7620AE:
-		hwsbase = 0x38;
-		hwebase = 0x3a;
-		vwsbase = vwebase = 0x05;
-		break;
+	case SEN_OV8600:
 	case SEN_OV6620:
-		hwsbase = 0x38;
-		hwebase = 0x3a;
-		vwsbase = 0x05;
-		vwebase = 0x06;
+	case SEN_OV6630:
+		rc = set_ov_sensor_window(ov511, width, height, mode, sub_flag);
 		break;
-	case SEN_OV7620:
-		hwsbase = 0x2c;
-		hwebase = 0x2d;
-		vwsbase = vwebase = 0x05;
+	case SEN_KS0127:
+	case SEN_KS0127B:
+		err("KS0127-series decoders not supported yet");
+		rc = -EINVAL;
+		break;
+	case SEN_SAA7111A:
+//		rc = mode_init_saa_sensor_regs(ov511, width, height, mode, 
+//					       sub_flag);
+
+		PDEBUG(1, "SAA status = 0X%x", ov51x_i2c_read(ov511, 0x1f));
 		break;
 	default:
-		err("Invalid sensor");
-		return -EINVAL;
+		err("Unknown sensor");
+		rc = -EINVAL;
 	}
 
-	/* Bit 5 of COM C register varies with sensor */ 
-	if (ov511->sensor == SEN_OV6620) {
-		if (width > 176 && height > 144) {  /* CIF */
-			ov511_i2c_write(dev, 0x14, 0x04);
-			hwscale = 1;
-			vwscale = 1;  /* The datasheet says 0; it's wrong */
-			hwsize = 352;
-			vwsize = 288;
-		} else {			    /* QCIF */
-			ov511_i2c_write(dev, 0x14, 0x24);
-			hwsize = 176;
-			vwsize = 144;
-		}
-	}
-	else {
-		if (width > 320 && height > 240) {  /* VGA */
-			ov511_i2c_write(dev, 0x14, 0x04);
-			hwscale = 2;
-			vwscale = 1;
-			hwsize = 640;
-			vwsize = 480;
-		} else {			    /* QVGA */
-			ov511_i2c_write(dev, 0x14, 0x24);
-			hwscale = 1;
-			hwsize = 320;
-			vwsize = 240;
-		}	
-	}
+	if (FATAL_ERROR(rc))
+		return rc;
 
-	/* FIXME! - This needs to be changed to support 160x120 and 6620!!! */
-	if (sub_flag) {
-		ov511_i2c_write(dev, 0x17, hwsbase+(ov511->subx>>hwscale));
-		ov511_i2c_write(dev, 0x18, hwebase+((ov511->subx+ov511->subw)>>hwscale));
-		ov511_i2c_write(dev, 0x19, vwsbase+(ov511->suby>>vwscale));
-		ov511_i2c_write(dev, 0x1a, vwebase+((ov511->suby+ov511->subh)>>vwscale));
-	} else {
-		ov511_i2c_write(dev, 0x17, hwsbase);
-		ov511_i2c_write(dev, 0x18, hwebase + (hwsize>>hwscale));
-		ov511_i2c_write(dev, 0x19, vwsbase);
-		ov511_i2c_write(dev, 0x1a, vwebase + (vwsize>>vwscale));
-	}
+	/* Sensor-independent settings */
+	rc = sensor_set_auto_brightness(ov511, ov511->auto_brt);
+	if (FATAL_ERROR(rc))
+		return rc;
 
-	for (i = 0; mlist[i].width; i++) {
-		int lncnt, pxcnt, clock;
+	rc = sensor_set_auto_exposure(ov511, ov511->auto_exp);
+	if (FATAL_ERROR(rc))
+		return rc;
 
-		if (width != mlist[i].width || height != mlist[i].height)
-			continue;
+	rc = sensor_set_banding_filter(ov511, bandingfilter);
+	if (FATAL_ERROR(rc))
+		return rc;
 
-		if (!mlist[i].color && mode != VIDEO_PALETTE_GREY)
-			continue;
+	if (ov511->lightfreq) {
+		rc = sensor_set_light_freq(ov511, lightfreq);
+		if (FATAL_ERROR(rc))
+			return rc;
+	}
 
-		/* Here I'm assuming that snapshot size == image size.
-		 * I hope that's always true. --claudio
-		 */
-		pxcnt = sub_flag ? (ov511->subw >> 3) - 1 : mlist[i].pxcnt;
-		lncnt = sub_flag ? (ov511->subh >> 3) - 1 : mlist[i].lncnt;
+	rc = sensor_set_backlight(ov511, ov511->backlight);
+	if (FATAL_ERROR(rc))
+		return rc;
 
-		ov511_reg_write(dev, 0x12, pxcnt);
-		ov511_reg_write(dev, 0x13, lncnt);
-		ov511_reg_write(dev, 0x14, mlist[i].pxdv);
-		ov511_reg_write(dev, 0x15, mlist[i].lndv);
-		ov511_reg_write(dev, 0x18, mlist[i].m420);
-
-		/* Snapshot additions */
-		ov511_reg_write(dev, 0x1a, pxcnt);
-		ov511_reg_write(dev, 0x1b, lncnt);
-                ov511_reg_write(dev, 0x1c, mlist[i].pxdv);
-                ov511_reg_write(dev, 0x1d, mlist[i].lndv);
-
-		/* Calculate and set the clock divisor */
-		clock = ((sub_flag ? ov511->subw * ov511->subh : width * height)
-			* (mlist[i].color ? 3 : 2) / 2) / 66000;
-#if 0
-		clock *= cams;
-#endif
-		ov511_i2c_write(dev, 0x11, clock);
+	return 0;
+}
+
+/* This sets the default image parameters (Size = max, RGB24). This is
+ * useful for apps that use read() and do not set these.
+ */
+static int 
+ov51x_set_default_params(struct usb_ov511 *ov511)
+{
+	int i;
+
+	PDEBUG(3, "%dx%d, RGB24", ov511->maxwidth, ov511->maxheight);
 
-		/* We only have code to convert GBR -> RGB24 */
-		if ((mode == VIDEO_PALETTE_RGB24) && sensor_gbr)
-			ov511_i2c_write(dev, 0x12, mlist[i].common_A | (testpat?0x0a:0x08));
+	/* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used
+	 * (using read() instead). */
+	for (i = 0; i < OV511_NUMFRAMES; i++) {
+		ov511->frame[i].width = ov511->maxwidth;
+		ov511->frame[i].height = ov511->maxheight;
+		ov511->frame[i].bytes_read = 0;
+		if (force_palette)
+			ov511->frame[i].format = force_palette;
 		else
-			ov511_i2c_write(dev, 0x12, mlist[i].common_A | (testpat?0x02:0x00));
+			ov511->frame[i].format = VIDEO_PALETTE_RGB24;
+		ov511->frame[i].depth = ov511_get_depth(ov511->frame[i].format);
+	}
+
+	/* Initialize to max width/height, RGB24 */
+	if (mode_init_regs(ov511, ov511->maxwidth, ov511->maxheight,
+			   ov511->frame[0].format, 0) < 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+/**********************************************************************
+ *
+ * Video decoder stuff
+ *
+ **********************************************************************/
 
-		/* 7620/6620 don't have register 0x35, so play it safe */
-		if (ov511->sensor == SEN_OV7610 ||
-		    ov511->sensor == SEN_OV7620AE)
-			ov511_i2c_write(dev, 0x35, mlist[i].common_L);
+/* Set analog input port of decoder */
+static int 
+decoder_set_input(struct usb_ov511 *ov511, int input)
+{
+	PDEBUG(4, "port %d", input);
 
+	switch (ov511->sensor) {
+	case SEN_SAA7111A:
+	{
+		/* Select mode */
+		ov51x_i2c_write_mask(ov511, 0x02, input, 0x07);
+		/* Bypass chrominance trap for modes 4..7 */
+		ov51x_i2c_write_mask(ov511, 0x09,
+				     (input > 3) ? 0x80:0x00, 0x80);
 		break;
 	}
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* Get ASCII name of video input */
+static int 
+decoder_get_input_name(struct usb_ov511 *ov511, int input, char *name)
+{
+	switch (ov511->sensor) {
+	case SEN_SAA7111A:
+	{
+		if (input < 0 || input > 7)
+			return -EINVAL;
+		else if (input < 4)
+			sprintf(name, "CVBS-%d", input);
+		else // if (input < 8)
+			sprintf(name, "S-Video-%d", input - 4);
 
-	if (compress) {
-		ov511_reg_write(dev, 0x78, 0x03); // Turn on Y compression
-		ov511_reg_write(dev, 0x79, 0x00); // Disable LUTs
+		break;
+	}
+	default:
+		sprintf(name, "%s", "Camera");
 	}
 
-	if (ov511_restart(ov511->dev) < 0)
-		return -EIO;
+	return 0;
+}
 
-	if (mlist[i].width == 0) {
-		err("Unknown mode (%d, %d): %d", width, height, mode);
+/* Set norm (NTSC, PAL, SECAM, AUTO) */
+static int 
+decoder_set_norm(struct usb_ov511 *ov511, int norm)
+{
+	PDEBUG(4, "%d", norm);
+
+	switch (ov511->sensor) {
+	case SEN_SAA7111A:
+	{
+		int reg_8, reg_e;
+
+		if (norm == VIDEO_MODE_NTSC) {
+			reg_8 = 0x40;	/* 60 Hz */
+			reg_e = 0x00;	/* NTSC M / PAL BGHI */
+		} else if (norm == VIDEO_MODE_PAL) {
+			reg_8 = 0x00;	/* 50 Hz */
+			reg_e = 0x00;	/* NTSC M / PAL BGHI */	
+		} else if (norm == VIDEO_MODE_AUTO) {
+			reg_8 = 0x80;	/* Auto field detect */
+			reg_e = 0x00;	/* NTSC M / PAL BGHI */
+		} else if (norm == VIDEO_MODE_SECAM) {
+			reg_8 = 0x00;	/* 50 Hz */
+			reg_e = 0x50;	/* SECAM / PAL 4.43 */
+		} else {
+			return -EINVAL;
+		}
+
+		ov51x_i2c_write_mask(ov511, 0x08, reg_8, 0xc0);
+		ov51x_i2c_write_mask(ov511, 0x0e, reg_e, 0x70);
+		break;
+	}
+	default:
 		return -EINVAL;
 	}
 
-#ifdef OV511_DEBUG
-	if (debug >= 5)
-		ov511_dump_i2c_regs(dev);
-#endif
-
 	return 0;
 }
 
+
 /**********************************************************************
  *
  * Color correction functions
@@ -1169,7 +3237,7 @@
 
 static inline void
 ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v, 
-	int rowPixels, unsigned char * rgb, int bits)
+		     int rowPixels, unsigned char * rgb, int bits)
 {
 	const int rvScale = 91881;
 	const int guScale = -22553;
@@ -1192,34 +3260,104 @@
 
 	if (bits == 24) {
 		/* Write out top two pixels */
-		rgb[0] = LIMIT(b+yTL); rgb[1] = LIMIT(g+yTL); rgb[2] = LIMIT(r+yTL);
-		rgb[3] = LIMIT(b+yTR); rgb[4] = LIMIT(g+yTR); rgb[5] = LIMIT(r+yTR);
+		rgb[0] = LIMIT(b+yTL); rgb[1] = LIMIT(g+yTL);
+		rgb[2] = LIMIT(r+yTL);
+
+		rgb[3] = LIMIT(b+yTR); rgb[4] = LIMIT(g+yTR);
+		rgb[5] = LIMIT(r+yTR);
 
 		/* Skip down to next line to write out bottom two pixels */
 		rgb += 3 * rowPixels;
-		rgb[0] = LIMIT(b+yBL); rgb[1] = LIMIT(g+yBL); rgb[2] = LIMIT(r+yBL);
-		rgb[3] = LIMIT(b+yBR); rgb[4] = LIMIT(g+yBR); rgb[5] = LIMIT(r+yBR);
+		rgb[0] = LIMIT(b+yBL); rgb[1] = LIMIT(g+yBL);
+		rgb[2] = LIMIT(r+yBL);
+
+		rgb[3] = LIMIT(b+yBR); rgb[4] = LIMIT(g+yBR);
+		rgb[5] = LIMIT(r+yBR);
 	} else if (bits == 16) {
 		/* Write out top two pixels */
-		rgb[0] = ((LIMIT(b+yTL) >> 3) & 0x1F) | ((LIMIT(g+yTL) << 3) & 0xE0);
-		rgb[1] = ((LIMIT(g+yTL) >> 5) & 0x07) | (LIMIT(r+yTL) & 0xF8);
-
-		rgb[2] = ((LIMIT(b+yTR) >> 3) & 0x1F) | ((LIMIT(g+yTR) << 3) & 0xE0);
-		rgb[3] = ((LIMIT(g+yTR) >> 5) & 0x07) | (LIMIT(r+yTR) & 0xF8);
+		rgb[0] = ((LIMIT(b+yTL) >> 3) & 0x1F) 
+			| ((LIMIT(g+yTL) << 3) & 0xE0);
+		rgb[1] = ((LIMIT(g+yTL) >> 5) & 0x07)
+			| (LIMIT(r+yTL) & 0xF8);
+
+		rgb[2] = ((LIMIT(b+yTR) >> 3) & 0x1F) 
+			| ((LIMIT(g+yTR) << 3) & 0xE0);
+		rgb[3] = ((LIMIT(g+yTR) >> 5) & 0x07) 
+			| (LIMIT(r+yTR) & 0xF8);
 
 		/* Skip down to next line to write out bottom two pixels */
 		rgb += 2 * rowPixels;
 
-		rgb[0] = ((LIMIT(b+yBL) >> 3) & 0x1F) | ((LIMIT(g+yBL) << 3) & 0xE0);
-		rgb[1] = ((LIMIT(g+yBL) >> 5) & 0x07) | (LIMIT(r+yBL) & 0xF8);
+		rgb[0] = ((LIMIT(b+yBL) >> 3) & 0x1F)
+			| ((LIMIT(g+yBL) << 3) & 0xE0);
+		rgb[1] = ((LIMIT(g+yBL) >> 5) & 0x07)
+			| (LIMIT(r+yBL) & 0xF8);
+
+		rgb[2] = ((LIMIT(b+yBR) >> 3) & 0x1F)
+			| ((LIMIT(g+yBR) << 3) & 0xE0);
+		rgb[3] = ((LIMIT(g+yBR) >> 5) & 0x07)
+			| (LIMIT(r+yBR) & 0xF8);
+	}
+}
+
+/**********************************************************************
+ *
+ * Raw data parsing
+ *
+ **********************************************************************/
+
+/* Copies a 64-byte segment at pIn to an 8x8 block at pOut. The width of the
+ * array at pOut is specified by w.
+ */
+static inline void 
+ov511_make_8x8(unsigned char *pIn, unsigned char *pOut, int w)
+{
+	unsigned char *pOut1 = pOut;
+	int x, y;
+
+	for (y = 0; y < 8; y++) {
+		pOut1 = pOut;
+		for (x = 0; x < 8; x++) {
+			*pOut1++ = *pIn++;
+		}
+		pOut += w;
+	}
+		
+}
+
+/*
+ * For RAW BW (YUV400) images, data shows up in 256 byte segments.
+ * The segments represent 4 squares of 8x8 pixels as follows:
+ *
+ *      0  1 ...  7    64  65 ...  71   ...  192 193 ... 199
+ *      8  9 ... 15    72  73 ...  79        200 201 ... 207
+ *           ...              ...                    ...
+ *     56 57 ... 63   120 121 ... 127        248 249 ... 255
+ *
+ */ 
+static void
+yuv400raw_to_yuv400p(struct ov511_frame *frame,
+		     unsigned char *pIn0, unsigned char *pOut0)
+{
+	int x, y;
+	unsigned char *pIn, *pOut, *pOutLine;
 
-		rgb[2] = ((LIMIT(b+yBR) >> 3) & 0x1F) | ((LIMIT(g+yBR) << 3) & 0xE0);
-		rgb[3] = ((LIMIT(g+yBR) >> 5) & 0x07) | (LIMIT(r+yBR) & 0xF8);
+	/* Copy Y */
+	pIn = pIn0;
+	pOutLine = pOut0;
+	for (y = 0; y < frame->rawheight - 1; y += 8) {
+		pOut = pOutLine;
+		for (x = 0; x < frame->rawwidth - 1; x += 8) {
+			ov511_make_8x8(pIn, pOut, frame->rawwidth);
+			pIn += 64;
+			pOut += 8;
+		}
+		pOutLine += 8 * frame->rawwidth;
 	}
 }
 
 /*
- * For a 640x480 YUV4:2:0 images, data shows up in 1200 384 byte segments.
+ * For YUV4:2:0 images, the data shows up in 384 byte segments.
  * The first 64 bytes of each segment are U, the next 64 are V.  The U and
  * V are arranged as follows:
  *
@@ -1239,7 +3377,9 @@
  *     56 57 ... 63   120 121 ... 127   ...  248 249 ... 255
  *
  * Note that the U and V data in one segment represents a 16 x 16 pixel
- * area, but the Y data represents a 32 x 8 pixel area.
+ * area, but the Y data represents a 32 x 8 pixel area. If the width is not an
+ * even multiple of 32, the extra 8x8 blocks within a 32x8 block belong to the
+ * next horizontal stripe.
  *
  * If dumppix module param is set, _parse_data just dumps the incoming segments,
  * verbatim, in order, into the frame. When used with vidcat -f ppm -s 640x480
@@ -1248,391 +3388,504 @@
  * this data is scrambled.
  */
 
-#define HDIV 8
-#define WDIV (256/HDIV)
-
+/* Converts from raw, uncompressed segments at pIn0 to a YUV420P frame at pOut0.
+ *
+ * FIXME: Currently only handles width and height that are multiples of 16
+ */
 static void
-ov511_parse_gbr422_to_rgb24(unsigned char *pIn0, unsigned char *pOut0,
-		       int iOutY, int iOutUV, int iHalf, int iWidth)
+yuv420raw_to_yuv420p(struct ov511_frame *frame,
+		     unsigned char *pIn0, unsigned char *pOut0)
 {
-	int k, l, m;
-	unsigned char *pIn;
-	unsigned char *pOut, *pOut1;
+	int k, x, y;
+	unsigned char *pIn, *pOut, *pOutLine;
+	const unsigned int a = frame->rawwidth * frame->rawheight;
+	const unsigned int w = frame->rawwidth / 2;
 
+	/* Copy U and V */
 	pIn = pIn0;
-	pOut = pOut0 + iOutUV + (force_rgb ? 2 : 0);
-		
-	for (k = 0; k < 8; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) {
-			*pOut1 = *(pOut1 + 3) = *(pOut1 + iWidth*3) =
-				*(pOut1 + iWidth*3 + 3) = *pIn++;
-			pOut1 += 6;
+	pOutLine = pOut0 + a;
+	for (y = 0; y < frame->rawheight - 1; y += 16) {
+		pOut = pOutLine;
+		for (x = 0; x < frame->rawwidth - 1; x += 16) {
+			ov511_make_8x8(pIn, pOut, w);
+			ov511_make_8x8(pIn + 64, pOut + a/4, w);
+			pIn += 384;
+			pOut += 8;
 		}
-		pOut += iWidth*3*2;
+		pOutLine += 8 * w;
 	}
 
-	pIn = pIn0 + 64;
-	pOut = pOut0 + iOutUV + (force_rgb ? 0 : 2);
-	for (k = 0; k < 8; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) {
-			*pOut1 = *(pOut1 + 3) = *(pOut1 + iWidth*3) =
-				*(pOut1 + iWidth*3 + 3) = *pIn++;
-			pOut1 += 6;
+	/* Copy Y */
+	pIn = pIn0 + 128;
+	pOutLine = pOut0;
+	k = 0;
+	for (y = 0; y < frame->rawheight - 1; y += 8) {
+		pOut = pOutLine;
+		for (x = 0; x < frame->rawwidth - 1; x += 8) {
+			ov511_make_8x8(pIn, pOut, frame->rawwidth);
+			pIn += 64;
+			pOut += 8;
+			if ((++k) > 3) {
+				k = 0;
+				pIn += 128;
+			}
+		}
+		pOutLine += 8 * frame->rawwidth;
+	}
+}
+
+/*
+ * fixFrameRGBoffset--
+ * My camera seems to return the red channel about 1 pixel
+ * low, and the blue channel about 1 pixel high. After YUV->RGB
+ * conversion, we can correct this easily. OSL 2/24/2000.
+ */
+static void 
+fixFrameRGBoffset(struct ov511_frame *frame)
+{
+	int x, y;
+	int rowBytes = frame->width*3, w = frame->width;
+	unsigned char *rgb = frame->data;
+	const int shift = 1;  /* Distance to shift pixels by, vertically */
+
+	/* Don't bother with little images */
+	if (frame->width < 400) 
+		return;
+
+	/* This only works with RGB24 */
+	if (frame->format != VIDEO_PALETTE_RGB24)
+		return;
+
+	/* Shift red channel up */
+	for (y = shift; y < frame->height; y++)	{
+		int lp = (y-shift)*rowBytes;     /* Previous line offset */
+		int lc = y*rowBytes;             /* Current line offset */
+		for (x = 0; x < w; x++)
+			rgb[lp+x*3+2] = rgb[lc+x*3+2]; /* Shift red up */
+	}
+
+	/* Shift blue channel down */
+	for (y = frame->height-shift-1; y >= 0; y--) {
+		int ln = (y + shift) * rowBytes;  /* Next line offset */
+		int lc = y * rowBytes;            /* Current line offset */
+		for (x = 0; x < w; x++)
+			rgb[ln+x*3+0] = rgb[lc+x*3+0]; /* Shift blue down */
+	}
+}
+
+/**********************************************************************
+ *
+ * Decompression
+ *
+ **********************************************************************/
+
+/* Chooses a decompression module, locks it, and sets ov511->decomp_ops
+ * accordingly. Returns -ENXIO if decompressor is not available, otherwise
+ * returns 0 if no other error.
+ */
+static int 
+ov51x_request_decompressor(struct usb_ov511 *ov511)
+{
+	if (!ov511)
+		return -ENODEV;
+
+	if (ov511->decomp_ops) {
+		err("ERROR: Decompressor already requested!");
+		return -EINVAL;
+	}
+
+	lock_kernel();
+
+	/* Try to get MMX, and fall back on no-MMX if necessary */
+	if (ov511->bridge == BRG_OV511 || ov511->bridge == BRG_OV511PLUS) {
+		if (ov511_mmx_decomp_ops) {
+			PDEBUG(3, "Using OV511 MMX decompressor");
+			ov511->decomp_ops = ov511_mmx_decomp_ops;
+		} else if (ov511_decomp_ops) {
+			PDEBUG(3, "Using OV511 decompressor");
+			ov511->decomp_ops = ov511_decomp_ops;
+		} else {
+			err("No decompressor available");
+		}
+	} else if (ov511->bridge == BRG_OV518 ||
+		   ov511->bridge == BRG_OV518PLUS) {
+		if (ov518_mmx_decomp_ops) {
+			PDEBUG(3, "Using OV518 MMX decompressor");
+			ov511->decomp_ops = ov518_mmx_decomp_ops;
+		} else if (ov518_decomp_ops) {
+			PDEBUG(3, "Using OV518 decompressor");
+			ov511->decomp_ops = ov518_decomp_ops;
+		} else {
+			err("No decompressor available");
 		}
-		pOut += iWidth*3*2;
+	} else {
+		err("Unknown bridge");
 	}
 
-	pIn = pIn0 + 128;
-	pOut = pOut0 + iOutY + 1;
-	for (k = 0; k < 4; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) {
-			for (m = 0; m < 8; m++) {
-				*pOut1 = *pIn++;
-				pOut1 += 3;
-			}
-			pOut1 += (iWidth - 8) * 3;
+	if (ov511->decomp_ops) {
+		if (!ov511->decomp_ops->decomp_lock) {
+			ov511->decomp_ops = NULL;
+			unlock_kernel();
+			return -ENOSYS;
 		}
-		pOut += 8 * 3;
+		ov511->decomp_ops->decomp_lock();
+		unlock_kernel();
+		return 0;
+	} else {
+		unlock_kernel();
+		return -ENXIO;
 	}
 }
 
-static void
-ov511_parse_yuv420_to_rgb(unsigned char *pIn0, unsigned char *pOut0,
-		       int iOutY, int iOutUV, int iHalf, int iWidth, int bits)
+/* Unlocks decompression module and nulls ov511->decomp_ops. Safe to call even
+ * if ov511->decomp_ops is NULL.
+ */
+static void 
+ov51x_release_decompressor(struct usb_ov511 *ov511)
 {
-	int k, l, m;
-	int bytes = bits >> 3;
-	unsigned char *pIn;
-	unsigned char *pOut, *pOut1;
-
-	/* Just copy the Y's if in the first stripe */
-	if (!iHalf) {
-		pIn = pIn0 + 128;
-		pOut = pOut0 + iOutY;
-		for (k = 0; k < 4; k++) {
-			pOut1 = pOut;
-			for (l = 0; l < 8; l++) {
-				for (m = 0; m < 8; m++) {
-					*pOut1 = *pIn++;
-					pOut1 += bytes;
-				}
-				pOut1 += (iWidth - 8) * bytes;
-			}
-			pOut += 8 * bytes;
-		}
-	}
+	int released = 0;	/* Did we actually do anything? */
 
-	/* Use the first half of VUs to calculate value */
-	pIn = pIn0;
-	pOut = pOut0 + iOutUV;
-	for (l = 0; l < 4; l++) {
-		for (m=0; m<8; m++) {
-			int y00 = *(pOut);
-			int y01 = *(pOut+bytes);
-			int y10 = *(pOut+iWidth*bytes);
-			int y11 = *(pOut+iWidth*bytes+bytes);
-			int v   = *(pIn+64) - 128;
-			int u   = *pIn++ - 128;
-			ov511_move_420_block(y00, y01, y10, y11, u, v, iWidth,
-				pOut, bits);
-			pOut += 2 * bytes;
-		}
-		pOut += (iWidth*2 - 16) * bytes;
+	if (!ov511)
+		return;
+
+	lock_kernel();
+
+	if (ov511->decomp_ops && ov511->decomp_ops->decomp_unlock) {
+		ov511->decomp_ops->decomp_unlock();
+		released = 1;
 	}
 
-	/* Just copy the other UV rows */
-	for (l = 0; l < 4; l++) {
-		for (m = 0; m < 8; m++) {
-			*pOut++ = *(pIn + 64);
-			*pOut = *pIn++;
-			pOut += 2 * bytes - 1;
-		}
-		pOut += (iWidth*2 - 16) * bytes;
-	}
-
-	/* Calculate values if it's the second half */
-	if (iHalf) {
-		pIn = pIn0 + 128;
-		pOut = pOut0 + iOutY;
-		for (k = 0; k < 4; k++) {
-			pOut1 = pOut;
-			for (l=0; l<4; l++) {
-				for (m=0; m<4; m++) {
-					int y10 = *(pIn+8);
-					int y00 = *pIn++;
-					int y11 = *(pIn+8);
-					int y01 = *pIn++;
-					int v   = *pOut1 - 128;
-					int u   = *(pOut1+1) - 128;
-					ov511_move_420_block(y00, y01, y10,
-						y11, u, v, iWidth, pOut1, bits);
-					pOut1 += 2 * bytes;
-				}
-				pOut1 += (iWidth*2 - 8) * bytes;
-				pIn += 8;
-			}
-			pOut += 8 * bytes;
-		}
+	ov511->decomp_ops = NULL;
+	
+	unlock_kernel();
+
+	if (released)
+		PDEBUG(3, "Decompressor released");
+}
+
+static void 
+ov51x_decompress(struct usb_ov511 *ov511, struct ov511_frame *frame,
+		 unsigned char *pIn0, unsigned char *pOut0)
+{
+	if (!ov511->decomp_ops)
+		if (ov51x_request_decompressor(ov511))
+			return;
+
+	PDEBUG(4, "Decompressing %d bytes", frame->bytes_recvd);
+
+	if (frame->format == VIDEO_PALETTE_GREY 
+	    && ov511->decomp_ops->decomp_400) {
+		int ret = ov511->decomp_ops->decomp_400(
+			pIn0,
+			pOut0,
+			frame->rawwidth,
+			frame->rawheight,
+			frame->bytes_recvd);
+		PDEBUG(4, "DEBUG: decomp_400 returned %d", ret);
+	} else if (ov511->decomp_ops->decomp_420) {
+		int ret = ov511->decomp_ops->decomp_420(
+			pIn0,
+			pOut0,
+			frame->rawwidth,
+			frame->rawheight,
+			frame->bytes_recvd);
+		PDEBUG(4, "DEBUG: decomp_420 returned %d", ret);
+	} else {
+		err("Decompressor does not support this format");
 	}
 }
 
-static void
-ov511_dumppix(unsigned char *pIn0, unsigned char *pOut0,
-	      int iOutY, int iOutUV, int iHalf, int iWidth)
-{
-	int i, j, k;
-	unsigned char *pIn, *pOut, *pOut1;
+/**********************************************************************
+ *
+ * Format conversion
+ *
+ **********************************************************************/
 
-	switch (dumppix) {
-	case 1: /* Just dump YUV data straight out for debug */
-		pOut0 += iOutY;
-		for (i = 0; i < HDIV; i++) {
-			for (j = 0; j < WDIV; j++) {
-				*pOut0++ = *pIn0++;
-				*pOut0++ = *pIn0++;
-				*pOut0++ = *pIn0++;
-			}
-			pOut0 += (iWidth - WDIV) * 3;
-		}
-		break;
-	case 2: /* This converts the Y data to "black-and-white" RGB data */
-		/* Useful for experimenting with compression */
-		pIn = pIn0 + 128;
-		pOut = pOut0 + iOutY;
-		for (i = 0; i < 4; i++) {
-			pOut1 = pOut;
-			for (j = 0; j < 8; j++) {
-				for (k = 0; k < 8; k++) {
-					*pOut1++ = *pIn;
-					*pOut1++ = *pIn;
-					*pOut1++ = *pIn++;
-				}
-				pOut1 += (iWidth - 8) * 3;
-			}
-			pOut += 8 * 3;
-		}
-		break;
-	case 3: /* This will dump only the Y channel data stream as-is */
-		pIn = pIn0 + 128;
-		pOut = pOut0 + output_offset;
-		for (i = 0; i < 256; i++) {
-			*pOut++ = *pIn;
-			*pOut++ = *pIn;
-			*pOut++ = *pIn++;
-			output_offset += 3;
-		}
-		break;
-	} /* End switch (dumppix) */
-}
+/* Converts from planar YUV420 to RGB24. */
+static void 
+yuv420p_to_rgb(struct ov511_frame *frame,
+	       unsigned char *pIn0, unsigned char *pOut0, int bits)
+{
+	const int numpix = frame->width * frame->height;
+	const int bytes = bits >> 3;
+	int i, j, y00, y01, y10, y11, u, v;
+	unsigned char *pY = pIn0;
+	unsigned char *pU = pY + numpix;
+	unsigned char *pV = pU + numpix / 4;
+	unsigned char *pOut = pOut0;
+
+	for (j = 0; j <= frame->height - 2; j += 2) {
+		for (i = 0; i <= frame->width - 2; i += 2) {
+			y00 = *pY;
+			y01 = *(pY + 1);
+			y10 = *(pY + frame->width);
+			y11 = *(pY + frame->width + 1);
+			u = (*pU++) - 128;
+			v = (*pV++) - 128;
 
-/* This converts YUV420 segments to YUYV */
-static void
-ov511_parse_data_yuv422(unsigned char *pIn0, unsigned char *pOut0,
-		        int iOutY, int iOutUV, int iWidth)
-{
-	int k, l, m;
-	unsigned char *pIn, *pOut, *pOut1;
+			ov511_move_420_block(y00, y01, y10, y11, u, v,
+					     frame->width, pOut, bits);
+	
+			pY += 2;
+			pOut += 2 * bytes;
 
-	pIn = pIn0 + 128;
-	pOut = pOut0 + iOutY;
-	for (k = 0; k < 4; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) {
-			for (m = 0; m < 8; m++) {
-				*pOut1 = (*pIn++);
-				pOut1 += 2;
-			}
-			pOut1 += (iWidth - 8) * 2;
 		}
-		pOut += 8 * 2;
+		pY += frame->width;
+		pOut += frame->width * bytes;
 	}
+}
 
-	pIn = pIn0;
-	pOut = pOut0 + iOutUV + 1;
-	for (l = 0; l < 8; l++) {
-		for (m=0; m<8; m++) {
-			int v   = *(pIn+64);
-			int u   = *pIn++;
+/* Converts from planar YUV420 to YUV422 (YUYV). */
+static void
+yuv420p_to_yuv422(struct ov511_frame *frame,
+		  unsigned char *pIn0, unsigned char *pOut0)
+{
+	const int numpix = frame->width * frame->height;
+	int i, j;
+	unsigned char *pY = pIn0;
+	unsigned char *pU = pY + numpix;
+	unsigned char *pV = pU + numpix / 4;
+	unsigned char *pOut = pOut0;
+
+	for (i = 0; i < numpix; i++) {
+		*pOut = *(pY + i);
+		pOut += 2;
+	}
+
+	pOut = pOut0 + 1;
+	for (j = 0; j <= frame->height - 2 ; j += 2) {
+		for (i = 0; i <= frame->width - 2; i += 2) {
+			int u = *pU++;
+			int v = *pV++;
 			
 			*pOut = u;
 			*(pOut+2) = v;
-			*(pOut+iWidth) = u;
-			*(pOut+iWidth+2) = v;
+			*(pOut+frame->width*2) = u;
+			*(pOut+frame->width*2+2) = v;
 			pOut += 4;
 		}
-		pOut += (iWidth*4 - 32);
+		pOut += (frame->width * 2);
 	}
 }
 
+/* Converts pData from planar YUV420 to planar YUV422 **in place**. */
 static void
-ov511_parse_data_yuv420(unsigned char *pIn0, unsigned char *pOut0,
-		        int iOutY, int iOutUV, int iWidth, int iHeight)
+yuv420p_to_yuv422p(struct ov511_frame *frame, unsigned char *pData)
 {
-	int k, l, m;
-	unsigned char *pIn;
-	unsigned char *pOut, *pOut1;
-	unsigned a = iWidth * iHeight;
-	unsigned w = iWidth / 2;
-
-	pIn = pIn0;
-	pOut = pOut0 + iOutUV + a;
-	for (k = 0; k < 8; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) *pOut1++ = *pIn++;
+	const int numpix = frame->width * frame->height;
+	const int w = frame->width;
+	int j;
+	unsigned char *pIn, *pOut;
+
+	/* Clear U and V */
+	memset(pData + numpix + numpix / 2, 127, numpix / 2);
+
+	/* Convert V starting from beginning and working forward */
+	pIn = pData + numpix + numpix / 4;
+	pOut = pData + numpix +numpix / 2;
+	for (j = 0; j <= frame->height - 2; j += 2) {
+		memmove(pOut, pIn, w/2);
+		memmove(pOut + w/2, pIn, w/2);
+		pIn += w/2;
 		pOut += w;
 	}
 
-	pIn = pIn0 + 64;
-	pOut = pOut0 + iOutUV + a + a/4;
-	for (k = 0; k < 8; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) *pOut1++ = *pIn++;
-		pOut += w;
-	}
-
-	pIn = pIn0 + 128;
-	pOut = pOut0 + iOutY;
-	for (k = 0; k < 4; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) {
-			for (m = 0; m < 8; m++)
-				*pOut1++ =*pIn++;
-			pOut1 += iWidth - 8;
-		}
-		pOut += 8;
+	/* Convert U, starting from end and working backward */
+	pIn = pData + numpix + numpix / 4;
+	pOut = pData + numpix + numpix / 2;
+	for (j = 0; j <= frame->height - 2; j += 2) {
+		pIn -= w/2;
+		pOut -= w;
+		memmove(pOut, pIn, w/2);
+		memmove(pOut + w/2, pIn, w/2);
 	}
 }
 
+/* Fuses even and odd fields together, and doubles width.
+ * INPUT: an odd field followed by an even field at pIn0, in YUV planar format
+ * OUTPUT: a normal YUV planar image, with correct aspect ratio
+ */
 static void
-ov511_parse_data_yuv422p(unsigned char *pIn0, unsigned char *pOut0,
-		       int iOutY, int iOutUV, int iWidth, int iHeight)
+deinterlace(struct ov511_frame *frame, int rawformat,
+            unsigned char *pIn0, unsigned char *pOut0)
 {
-	int k, l, m;
-	unsigned char *pIn;
-	unsigned char *pOut, *pOut1;
-	unsigned a = iWidth * iHeight;
-	unsigned w = iWidth / 2;
+	const int fieldheight = frame->rawheight / 2;
+	const int fieldpix = fieldheight * frame->rawwidth;
+	const int w = frame->width;
+	int x, y;
+	unsigned char *pInEven, *pInOdd, *pOut;
 
-	pIn = pIn0;
-	pOut = pOut0 + iOutUV + a;
-	for (k = 0; k < 8; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) {
-			*pOut1 = *(pOut1 + w) = *pIn++;
-			pOut1++;
-		}
-		pOut += iWidth;
+	PDEBUG(5, "fieldheight=%d", fieldheight);
+
+	if (frame->rawheight != frame->height) {
+		err("invalid height");
+		return;
 	}
 
-	pIn = pIn0 + 64;
-	pOut = pOut0 + iOutUV + a + a/2;
-	for (k = 0; k < 8; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) {
-			*pOut1 = *(pOut1 + w) = *pIn++;
-			pOut1++;
-		}
-		pOut += iWidth;
+	if ((frame->rawwidth * 2) != frame->width) {
+		err("invalid width");
+		return;
 	}
 
-	pIn = pIn0 + 128;
-	pOut = pOut0 + iOutY;
-	for (k = 0; k < 4; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) {
-			for (m = 0; m < 8; m++)
-				*pOut1++ =*pIn++;
-			pOut1 += iWidth - 8;
+	/* Y */
+	pInOdd = pIn0;
+	pInEven = pInOdd + fieldpix;
+	pOut = pOut0;
+	for (y = 0; y < fieldheight; y++) {
+		for (x = 0; x < frame->rawwidth; x++) {
+			*pOut = *pInEven;
+			*(pOut+1) = *pInEven++;
+			*(pOut+w) = *pInOdd;
+			*(pOut+w+1) = *pInOdd++;
+			pOut += 2;
 		}
-		pOut += 8;
+		pOut += w;
 	}
-}
-
-/*
- * For 640x480 RAW BW images, data shows up in 1200 256 byte segments.
- * The segments represent 4 squares of 8x8 pixels as follows:
- *
- *      0  1 ...  7    64  65 ...  71   ...  192 193 ... 199
- *      8  9 ... 15    72  73 ...  79        200 201 ... 207
- *           ...              ...                    ...
- *     56 57 ... 63   120 121 ... 127        248 249 ... 255
- *
- */ 
-static void
-ov511_parse_data_grey(unsigned char *pIn0, unsigned char *pOut0,
-		      int iOutY, int iWidth)		    
-{
-	int k, l, m;
-	unsigned char *pIn;
-	unsigned char *pOut, *pOut1;
 
-	pIn = pIn0;
-	pOut = pOut0 + iOutY;
-	for (k = 0; k < 4; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) {
-			for (m = 0; m < 8; m++) {
-				*pOut1++ = *pIn++;
+	if (rawformat == RAWFMT_YUV420) {
+	/* U */
+		pInOdd = pIn0 + fieldpix * 2;
+		pInEven = pInOdd + fieldpix / 4;
+		for (y = 0; y < fieldheight / 2; y++) {
+			for (x = 0; x < frame->rawwidth / 2; x++) {
+				*pOut = *pInEven;
+				*(pOut+1) = *pInEven++;
+				*(pOut+w/2) = *pInOdd;
+				*(pOut+w/2+1) = *pInOdd++;
+				pOut += 2;
+			}
+			pOut += w/2;
+		}
+	/* V */
+		pInOdd = pIn0 + fieldpix * 2 + fieldpix / 2;
+		pInEven = pInOdd + fieldpix / 4;
+		for (y = 0; y < fieldheight / 2; y++) {
+			for (x = 0; x < frame->rawwidth / 2; x++) {
+				*pOut = *pInEven;
+				*(pOut+1) = *pInEven++;
+				*(pOut+w/2) = *pInOdd;
+				*(pOut+w/2+1) = *pInOdd++;
+				pOut += 2;
 			}
-			pOut1 += iWidth - 8;
+			pOut += w/2;
 		}
-		pOut += 8;
 	}
 }
 
-/*
- * fixFrameRGBoffset--
- * My camera seems to return the red channel about 1 pixel
- * low, and the blue channel about 1 pixel high. After YUV->RGB
- * conversion, we can correct this easily. OSL 2/24/2000.
+/* Post-processes the specified frame. This consists of:
+ * 	1. Decompress frame, if necessary
+ *	2. Deinterlace frame and scale to proper size, if necessary
+ * 	3. Convert from YUV planar to destination format, if necessary
+ * 	4. Fix the RGB offset, if necessary
  */
-static void fixFrameRGBoffset(struct ov511_frame *frame)
+static void 
+ov511_postprocess(struct usb_ov511 *ov511, struct ov511_frame *frame)
 {
-	int x, y;
-	int rowBytes = frame->width*3, w = frame->width;
-	unsigned char *rgb = frame->data;
-	const int shift = 1;  /* Distance to shift pixels by, vertically */
+	if (dumppix) {
+		memset(frame->data, 0, 
+			MAX_DATA_SIZE(ov511->maxwidth, ov511->maxheight));
+		PDEBUG(4, "Dumping %d bytes", frame->bytes_recvd);
+		memmove(frame->data, frame->rawdata, frame->bytes_recvd);
+		return;
+	}
+
+	/* YUV400 must be handled separately */
+	if (frame->format == VIDEO_PALETTE_GREY) {
+		/* Deinterlace frame, if necessary */
+		if (ov511->sensor == SEN_SAA7111A && frame->rawheight == 480) {
+			if (frame->compressed)
+				ov51x_decompress(ov511, frame, frame->rawdata,
+						 frame->tempdata);
+			else
+				yuv400raw_to_yuv400p(frame, frame->rawdata,
+						     frame->tempdata);
+
+			deinterlace(frame, RAWFMT_YUV400, frame->tempdata,
+			            frame->data);
+		} else {
+			if (frame->compressed)
+				ov51x_decompress(ov511, frame, frame->rawdata,
+						 frame->data);
+			else
+				yuv400raw_to_yuv400p(frame, frame->rawdata,
+						     frame->data);
+		}
 
-	/* Don't bother with little images */
-	if (frame->width < 400) 
 		return;
+	}
 
-	/* Shift red channel up */
-	for (y = shift; y < frame->height; y++)	{
-		int lp = (y-shift)*rowBytes;     /* Previous line offset */
-		int lc = y*rowBytes;             /* Current line offset */
-		for (x = 0; x < w; x++)
-			rgb[lp+x*3+2] = rgb[lc+x*3+2]; /* Shift red up */
+	/* Process frame->data to frame->rawdata */
+	if (frame->compressed)
+		ov51x_decompress(ov511, frame, frame->rawdata, frame->tempdata);
+	else
+		yuv420raw_to_yuv420p(frame, frame->rawdata, frame->tempdata);
+
+	/* Deinterlace frame, if necessary */
+	if (ov511->sensor == SEN_SAA7111A && frame->rawheight == 480) {
+		memmove(frame->rawdata, frame->tempdata,
+			MAX_RAW_DATA_SIZE(frame->width, frame->height));
+		deinterlace(frame, RAWFMT_YUV420, frame->rawdata,
+		            frame->tempdata);
 	}
 
-	/* Shift blue channel down */
-	for (y = frame->height-shift-1; y >= 0; y--) {
-		int ln = (y + shift) * rowBytes;  /* Next line offset */
-		int lc = y * rowBytes;            /* Current line offset */
-		for (x = 0; x < w; x++)
-			rgb[ln+x*3+0] = rgb[lc+x*3+0]; /* Shift blue down */
+	/* Frame should be (width x height) and not (rawwidth x rawheight) at
+         * this point. */
+
+#if 0
+	/* Clear output buffer for testing purposes */
+	memset(frame->data, 0, MAX_DATA_SIZE(frame->width, frame->height));
+#endif
+
+	/* Process frame->tempdata to frame->data */
+	switch (frame->format) {
+	case VIDEO_PALETTE_RGB565:
+		yuv420p_to_rgb(frame, frame->tempdata, frame->data, 16);
+		break;
+	case VIDEO_PALETTE_RGB24:
+		yuv420p_to_rgb(frame, frame->tempdata, frame->data, 24);
+		break;
+	case VIDEO_PALETTE_YUV422:
+	case VIDEO_PALETTE_YUYV:
+		yuv420p_to_yuv422(frame, frame->tempdata, frame->data);
+		break;
+	case VIDEO_PALETTE_YUV420:
+	case VIDEO_PALETTE_YUV420P:
+		memmove(frame->data, frame->tempdata,
+			MAX_RAW_DATA_SIZE(frame->width, frame->height));
+		break;
+	case VIDEO_PALETTE_YUV422P:
+		/* Data is converted in place, so copy it in advance */
+		memmove(frame->data, frame->tempdata,
+			MAX_RAW_DATA_SIZE(frame->width, frame->height));
+
+		yuv420p_to_yuv422p(frame, frame->data);
+		break;
+	default:
+		err("Cannot convert data to this format");
 	}
+
+	if (fix_rgb_offset)
+		fixFrameRGBoffset(frame);
 }
 
 /**********************************************************************
  *
- * OV511 data transfer, IRQ handler
+ * OV51x data transfer, IRQ handler
  *
  **********************************************************************/
 
-static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
+static int 
+ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
 {
 	unsigned char *cdata;
-	int i, totlen = 0;
-	int aPackNum[10];
+	int data_size, num, offset, i, totlen = 0;
+	int aPackNum[FRAMES_PER_DESC];
 	struct ov511_frame *frame;
-	unsigned char *pData;
-	int iPix;
+	struct timeval *ts;
+
+	PDEBUG(5, "Moving %d packets", urb->number_of_packets);
 
-	PDEBUG (4, "Moving %d packets", urb->number_of_packets);
+	data_size = ov511->packet_size - 1;
 
 	for (i = 0; i < urb->number_of_packets; i++) {
 		int n = urb->iso_frame_desc[i].actual_length;
@@ -1652,44 +3905,65 @@
 			PDEBUG(2, "data error: [%d] len=%d, status=%d", i, n, st);
 
 		frame = &ov511->frame[ov511->curframe];
-		
+
 		/* SOF/EOF packets have 1st to 8th bytes zeroed and the 9th
 		 * byte non-zero. The EOF packet has image width/height in the
-		 * 10th and 11th packets. The 9th bit is given as follows:
+		 * 10th and 11th bytes. The 9th byte is given as follows:
 		 *
 		 * bit 7: EOF
 		 *     6: compression enabled
 		 *     5: 422/420/400 modes
 		 *     4: 422/420/400 modes
 		 *     3: 1
-		 *     2: snapshot bottom on
+		 *     2: snapshot button on
 		 *     1: snapshot frame
 		 *     0: even/odd field
 		 */
 
+		if (printph) {
+			info("packet header (%3d): %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x",
+				cdata[ov511->packet_size - 1],
+				cdata[0], cdata[1], cdata[2], cdata[3], cdata[4], cdata[5],
+				cdata[6], cdata[7], cdata[8], cdata[9], cdata[10], cdata[11]);
+		}
+
 		/* Check for SOF/EOF packet */
-		if ((cdata[0] | cdata[1] | cdata[2] | cdata[3] | 
+		if ((cdata[0] | cdata[1] | cdata[2] | cdata[3] |
 		     cdata[4] | cdata[5] | cdata[6] | cdata[7]) ||
 		     (~cdata[8] & 0x08))
 			goto check_middle;
 
 		/* Frame end */
 		if (cdata[8] & 0x80) {
-			struct timeval *ts;
-
-			ts = (struct timeval *)(frame->data + MAX_FRAME_SIZE);
-			do_gettimeofday (ts);
-
-		 	PDEBUG(4, "Frame end, curframe = %d, packnum=%d, hw=%d, vw=%d",
-				ov511->curframe, (int)(cdata[ov511->packet_size - 1]),
-				(int)(cdata[9]), (int)(cdata[10]));
+			ts = (struct timeval *)(frame->data 
+			      + MAX_FRAME_SIZE(ov511->maxwidth, ov511->maxheight));
+			do_gettimeofday(ts);
+
+			/* Get the actual frame size from the EOF header */
+			frame->rawwidth = ((int)(cdata[9]) + 1) * 8;
+			frame->rawheight = ((int)(cdata[10]) + 1) * 8;
+
+	 		PDEBUG(4, "Frame end, curframe = %d, packnum=%d, hw=%d, vw=%d, recvd=%d",
+				ov511->curframe,
+				(int)(cdata[ov511->packet_size - 1]),
+				frame->rawwidth,
+				frame->rawheight,
+				frame->bytes_recvd);
+
+			/* Validate the header data */
+			RESTRICT_TO_RANGE(frame->rawwidth, ov511->minwidth, ov511->maxwidth);
+			RESTRICT_TO_RANGE(frame->rawheight, ov511->minheight, ov511->maxheight);
+
+			/* Don't allow byte count to exceed buffer size */
+			RESTRICT_TO_RANGE(frame->bytes_recvd,
+					  8, 
+					  MAX_RAW_DATA_SIZE(ov511->maxwidth,
+					                    ov511->maxheight));
 
 			if (frame->scanstate == STATE_LINES) {
 		    		int iFrameNext;
 
-				if (fix_rgb_offset)
-					fixFrameRGBoffset(frame);
-				frame->grabstate = FRAME_DONE;
+				frame->grabstate = FRAME_DONE;	// FIXME: Is this right?
 
 				if (waitqueue_active(&frame->wq)) {
 					frame->grabstate = FRAME_DONE;
@@ -1713,6 +3987,8 @@
 
 					ov511->curframe = -1;
 				}
+			} else {
+				PDEBUG(5, "Frame done, but not scanning");
 			}
 			/* Image corruption caused by misplaced frame->segment = 0
 			 * fixed by carlosf@conectiva.com.br
@@ -1721,13 +3997,6 @@
 			/* Frame start */
 			PDEBUG(4, "Frame start, framenum = %d", ov511->curframe);
 
-#if 0
-			/* Make sure no previous data carries over; necessary
-			 * for compression experimentation */
-			memset(frame->data, 0, MAX_DATA_SIZE);
-#endif
-			output_offset = 0;
-
 			/* Check to see if it's a snapshot frame */
 			/* FIXME?? Should the snapshot reset go here? Performance? */
 			if (cdata[8] & 0x02) {
@@ -1736,129 +4005,324 @@
 			}
 
 			frame->scanstate = STATE_LINES;
-			frame->segment = 0;
+			frame->bytes_recvd = 0;
+			frame->compressed = cdata[8] & 0x40;
 		}
 
 check_middle:
 		/* Are we in a frame? */
-		if (frame->scanstate != STATE_LINES)
+		if (frame->scanstate != STATE_LINES) {
+			PDEBUG(5, "Not in a frame; packet skipped");
+			continue;
+		}
+
+#if 0
+		/* Skip packet if first 9 bytes are zero. These are common, so
+		 * we use a less expensive test here instead of later */
+		if (frame->compressed) {
+			int b, skip = 1;
+
+			for (b = 0; b < 9; b++) { 
+				if (cdata[b])
+					skip=0;
+			}
+
+			if (skip) {
+				PDEBUG(5, "Skipping packet (all zero)");
+				continue;
+			}
+		}
+#endif
+		/* If frame start, skip header */
+		if (frame->bytes_recvd == 0)
+			offset = 9;
+		else
+			offset = 0;
+
+		num = n - offset - 1;
+
+		/* Dump all data exactly as received */
+		if (dumppix == 2) {
+			frame->bytes_recvd += n - 1;
+			if (frame->bytes_recvd <= MAX_RAW_DATA_SIZE(ov511->maxwidth, ov511->maxheight))
+				memmove(frame->rawdata + frame->bytes_recvd - (n - 1),
+					&cdata[0], n - 1);
+			else
+				PDEBUG(3, "Raw data buffer overrun!! (%d)",
+					frame->bytes_recvd
+					- MAX_RAW_DATA_SIZE(ov511->maxwidth,
+							    ov511->maxheight));
+		} else if (!frame->compressed && !remove_zeros) {
+			frame->bytes_recvd += num;
+			if (frame->bytes_recvd <= MAX_RAW_DATA_SIZE(ov511->maxwidth, ov511->maxheight))
+				memmove(frame->rawdata + frame->bytes_recvd - num,
+					&cdata[offset], num);
+			else
+				PDEBUG(3, "Raw data buffer overrun!! (%d)",
+					frame->bytes_recvd
+					- MAX_RAW_DATA_SIZE(ov511->maxwidth,
+							    ov511->maxheight));
+		} else { /* Remove all-zero FIFO lines (aligned 32-byte blocks) */
+			int b, in = 0, allzero, copied=0;
+			if (offset) {
+				frame->bytes_recvd += 32 - offset;	// Bytes out
+				memmove(frame->rawdata,
+					&cdata[offset], 32 - offset);
+				in += 32;
+			}
+
+			while (in < n - 1) {
+				allzero = 1;
+				for (b = 0; b < 32; b++) {
+					if (cdata[in + b]) {
+						allzero = 0;
+						break;
+					}
+				}
+
+				if (allzero) {
+					/* Don't copy it */
+				} else {
+					if (frame->bytes_recvd + copied + 32
+					    <= MAX_RAW_DATA_SIZE(ov511->maxwidth, ov511->maxheight)) {
+						memmove(frame->rawdata + frame->bytes_recvd + copied,
+							&cdata[in], 32);
+						copied += 32;
+					} else {
+						PDEBUG(3, "Raw data buffer overrun!!");
+					}
+				}
+				in += 32;
+			}
+
+			frame->bytes_recvd += copied;
+		}
+
+	}
+
+	PDEBUG(5, "pn: %d %d %d %d %d %d %d %d %d %d",
+		aPackNum[0], aPackNum[1], aPackNum[2], aPackNum[3], aPackNum[4],
+		aPackNum[5],aPackNum[6], aPackNum[7], aPackNum[8], aPackNum[9]);
+
+	return totlen;
+}
+
+static int 
+ov518_move_data(struct usb_ov511 *ov511, urb_t *urb)
+{
+	unsigned char *cdata;
+	int i, data_size, totlen = 0;
+	struct ov511_frame *frame;
+	struct timeval *ts;
+
+	PDEBUG(5, "Moving %d packets", urb->number_of_packets);
+
+	/* OV518(+) has no packet numbering */
+	data_size = ov511->packet_size;
+
+	for (i = 0; i < urb->number_of_packets; i++) {
+		int n = urb->iso_frame_desc[i].actual_length;
+		int st = urb->iso_frame_desc[i].status;
+
+		urb->iso_frame_desc[i].actual_length = 0;
+		urb->iso_frame_desc[i].status = 0;
+
+		cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+
+		if (!n) {
+			PDEBUG(4, "Zero-length packet");
 			continue;
+		}
+
+		if (ov511->curframe == -1) {
+			PDEBUG(4, "No frame currently active");
+			continue;
+		}
+
+		if (st)
+			PDEBUG(2, "data error: [%d] len=%d, status=%d", i, n, st);
+
+		frame = &ov511->frame[ov511->curframe];
+
+#if 0
+		{
+			int d;
+			/* Print all data */
+			for (d = 0; d <= data_size - 16; d += 16) {
+				info("%4x: %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x", d,
+					cdata[d], cdata[d+1], cdata[d+2], cdata[d+3],
+					cdata[d+4], cdata[d+5], cdata[d+6], cdata[d+7],
+					cdata[d+8], cdata[d+9], cdata[d+10], cdata[d+11],
+					cdata[d+12], cdata[d+13], cdata[d+14], cdata[d+15]);
+			}
+		}
+#endif
+
+		if (printph) {
+			info("packet header: %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x",
+				cdata[0], cdata[1], cdata[2], cdata[3], cdata[4], cdata[5],
+				cdata[6], cdata[7], cdata[8], cdata[9], cdata[10], cdata[11]);
+		}
 
-		/* Deal with leftover from last segment, if any */
-		if (frame->segment) {
-			pData = ov511->scratch;
-			iPix = -ov511->scratchlen;
-			memmove(pData + ov511->scratchlen, cdata,
-				iPix+frame->segsize);
+		/* A false positive here is likely, until OVT gives me
+		 * the definitive SOF/EOF format */
+		if ((!(cdata[0] | cdata[1] | cdata[2] | cdata[3] |
+		      cdata[5])) && cdata[6]) {
+			
+			if (frame->scanstate == STATE_LINES) {
+				PDEBUG(4, "Detected frame end/start");
+				goto eof;
+			} else { //scanstate == STATE_SCANNING
+				/* Frame start */
+				PDEBUG(4, "Frame start, framenum = %d", ov511->curframe);
+				goto sof;
+			}
 		} else {
-			pData = &cdata[iPix = 9];
-	 	}
+			goto check_middle;
+		}
+	
+eof:
+		ts = (struct timeval *)(frame->data
+		      + MAX_FRAME_SIZE(ov511->maxwidth, ov511->maxheight));
+		do_gettimeofday(ts);
+
+ 		PDEBUG(4, "Frame end, curframe = %d, hw=%d, vw=%d, recvd=%d",
+			ov511->curframe,
+			(int)(cdata[9]), (int)(cdata[10]), frame->bytes_recvd);
+
+		// FIXME: Since we don't know the header formats yet,
+		// there is no way to know what the actual image size is
+		frame->rawwidth = frame->width;
+		frame->rawheight = frame->height;
+
+		/* Validate the header data */
+		RESTRICT_TO_RANGE(frame->rawwidth, ov511->minwidth, ov511->maxwidth);
+		RESTRICT_TO_RANGE(frame->rawheight, ov511->minheight, ov511->maxheight);
+
+		/* Don't allow byte count to exceed buffer size */
+		RESTRICT_TO_RANGE(frame->bytes_recvd,
+				  8, 
+				  MAX_RAW_DATA_SIZE(ov511->maxwidth, ov511->maxheight));
 
-		/* Parse the segments */
-		while (iPix <= (ov511->packet_size - 1) - frame->segsize &&
-		    frame->segment < frame->width * frame->height / 256) {
-			int iSegY, iSegUV;
-			int iY, jY, iUV, jUV;
-			int iOutY, iOutYP, iOutUV, iOutUVP;
-			unsigned char *pOut;
-
-			iSegY = iSegUV = frame->segment;
-			pOut = frame->data;
-			frame->segment++;
-			iPix += frame->segsize;
-
-			/* Handle subwindow */
-			if (frame->sub_flag) {
-				int iSeg1;
-
-				iSeg1 = iSegY / (ov511->subw / 32);
-				iSeg1 *= frame->width / 32;
-				iSegY = iSeg1 + (iSegY % (ov511->subw / 32));
-				if (iSegY >= frame->width * ov511->subh / 256)
-					break;
-
-				iSeg1 = iSegUV / (ov511->subw / 16);
-				iSeg1 *= frame->width / 16;
-				iSegUV = iSeg1 + (iSegUV % (ov511->subw / 16));
-
-				pOut += (ov511->subx + ov511->suby * frame->width) *
-					(frame->depth >> 3);
-			}
-
-			/* 
-			 * i counts segment lines
-			 * j counts segment columns
-			 * iOut is the offset (in bytes) of the upper left corner
-			 */
-			iY = iSegY / (frame->width / WDIV);
-			jY = iSegY - iY * (frame->width / WDIV);
-			iOutYP = iY*HDIV*frame->width + jY*WDIV;
-			iOutY = iOutYP * (frame->depth >> 3);
-			iUV = iSegUV / (frame->width / WDIV * 2);
-			jUV = iSegUV - iUV * (frame->width / WDIV * 2);
-			iOutUVP = iUV*HDIV*2*frame->width + jUV*WDIV/2;
-			iOutUV = iOutUVP * (frame->depth >> 3);
-
-			switch (frame->format) {
-			case VIDEO_PALETTE_GREY:
-				ov511_parse_data_grey (pData, pOut, iOutY, frame->width);
-				break;
-			case VIDEO_PALETTE_RGB24:
-				if (dumppix)
-					ov511_dumppix(pData, pOut, iOutY, iOutUV,
-						iY & 1, frame->width);
-				else if (sensor_gbr)
-					ov511_parse_gbr422_to_rgb24(pData, pOut, iOutY, iOutUV,
-						iY & 1, frame->width);
-				else
-					ov511_parse_yuv420_to_rgb(pData, pOut, iOutY, iOutUV,
-						iY & 1, frame->width, 24);
-				break;
-			case VIDEO_PALETTE_RGB565:
-				ov511_parse_yuv420_to_rgb(pData, pOut, iOutY, iOutUV,
-					iY & 1, frame->width, 16);
-				break;
-			case VIDEO_PALETTE_YUV422:
-			case VIDEO_PALETTE_YUYV:
-				ov511_parse_data_yuv422(pData, pOut, iOutY, iOutUV, frame->width);
-				break;
-			case VIDEO_PALETTE_YUV420:
-				ov511_parse_data_yuv420 (pData, pOut, iOutYP, iUV*HDIV*frame->width/2 + jUV*WDIV/4,
-					frame->width, frame->height);
-				break;
-			case VIDEO_PALETTE_YUV422P:
-				ov511_parse_data_yuv422p (pData, pOut, iOutYP, iOutUVP/2,
-					frame->width, frame->height);
-				break;
-			default:
-				err("Unsupported format: %d", frame->format);
+		if (frame->scanstate == STATE_LINES) {
+	    		int iFrameNext;
+
+			frame->grabstate = FRAME_DONE;	// FIXME: Is this right?
+
+			if (waitqueue_active(&frame->wq)) {
+				frame->grabstate = FRAME_DONE;
+				wake_up_interruptible(&frame->wq);
 			}
 
-			pData = &cdata[iPix];
-		}
+			/* If next frame is ready or grabbing,
+			 * point to it */
+			iFrameNext = (ov511->curframe + 1) % OV511_NUMFRAMES;
+			if (ov511->frame[iFrameNext].grabstate == FRAME_READY
+			    || ov511->frame[iFrameNext].grabstate == FRAME_GRABBING) {
+				ov511->curframe = iFrameNext;
+				ov511->frame[iFrameNext].scanstate = STATE_SCANNING;
+				frame = &ov511->frame[iFrameNext];
+			} else {
+				if (frame->grabstate == FRAME_DONE) {
+					PDEBUG(4, "Frame done! congratulations");
+				} else {
+					PDEBUG(4, "Frame not ready? state = %d",
+						ov511->frame[iFrameNext].grabstate);
+				}
+
+				ov511->curframe = -1;
+				PDEBUG(4, "SOF dropped (no active frame)");
+				continue;  /* Nowhere to store this frame */
+			}
+		}
+		/* Image corruption caused by misplaced frame->segment = 0
+		 * fixed by carlosf@conectiva.com.br
+		 */
+sof:
+		PDEBUG(4, "Starting capture on frame %d", frame->framenum);
+// Snapshot not reverse-engineered yet.
+#if 0
+		/* Check to see if it's a snapshot frame */
+		/* FIXME?? Should the snapshot reset go here? Performance? */
+		if (cdata[8] & 0x02) {
+			frame->snapshot = 1;
+			PDEBUG(3, "snapshot detected");
+		}
+#endif
+		frame->scanstate = STATE_LINES;
+		frame->bytes_recvd = 0;
+//		frame->compressed = 1;
+
+check_middle:
+		/* Are we in a frame? */
+		if (frame->scanstate != STATE_LINES) {
+			PDEBUG(4, "scanstate: no SOF yet");
+			continue;
+		}
+
+		/* Dump all data exactly as received */
+		if (dumppix == 2) {
+			frame->bytes_recvd += n;
+			if (frame->bytes_recvd <= MAX_RAW_DATA_SIZE(ov511->maxwidth, ov511->maxheight))
+				memmove(frame->rawdata + frame->bytes_recvd - n,
+					&cdata[0], n);
+			else
+				PDEBUG(3, "Raw data buffer overrun!! (%d)",
+					frame->bytes_recvd
+					- MAX_RAW_DATA_SIZE(ov511->maxwidth,
+							    ov511->maxheight));
+		} else {
+			/* All incoming data are divided into 8-byte segments. If the
+			 * segment contains all zero bytes, it must be skipped. These
+			 * zero-segments allow the OV518 to mainain a constant data rate
+			 * regardless of the effectiveness of the compression. Segments
+			 * are aligned relative to the beginning of each isochronous
+			 * packet. The first segment is a header.
+			 */
+
+			int b, in = 0, allzero, copied=0;
+
+// Decompressor expects the header
+#if 0
+			if (frame->bytes_recvd == 0)
+				in += 8;  /* Skip header */
+#endif
+
+			while (in < n) {
+				allzero = 1;
+				for (b = 0; b < 8; b++) {
+					if (cdata[in + b]) {
+						allzero = 0;
+						break;
+					}
+				}
 
-		/* Save extra data for next time */
-		if (frame->segment < frame->width * frame->height / 256) {
-			ov511->scratchlen = (ov511->packet_size - 1) - iPix;
-			if (ov511->scratchlen < frame->segsize)
-				memmove(ov511->scratch, pData, ov511->scratchlen);
-			else
-				ov511->scratchlen = 0;
+				if (allzero) {
+				/* Don't copy it */
+				} else {
+					if (frame->bytes_recvd + copied + 8
+					    <= MAX_RAW_DATA_SIZE(ov511->maxwidth, ov511->maxheight)) {
+						memmove(frame->rawdata + frame->bytes_recvd + copied,
+							&cdata[in], 8);
+						copied += 8;
+					} else {
+						PDEBUG(3, "Raw data buffer overrun!!");
+					}
+				}
+				in += 8;
+			}
+			frame->bytes_recvd += copied;
 		}
 	}
 
-	PDEBUG(5, "pn: %d %d %d %d %d %d %d %d %d %d",
-	       aPackNum[0], aPackNum[1], aPackNum[2], aPackNum[3], aPackNum[4],
-	       aPackNum[5],aPackNum[6], aPackNum[7], aPackNum[8], aPackNum[9]);
-
 	return totlen;
 }
 
-static void ov511_isoc_irq(struct urb *urb)
+static void 
+ov511_isoc_irq(struct urb *urb)
 {
 	int len;
 	struct usb_ov511 *ov511;
-	struct ov511_sbuf *sbuf;
 
 	if (!urb->context) {
 		PDEBUG(4, "no context");
@@ -1876,60 +4340,86 @@
 		PDEBUG(4, "hmmm... not streaming, but got interrupt");
 		return;
 	}
-	
-	sbuf = &ov511->sbuf[ov511->cursbuf];
 
-	/* Copy the data received into our scratch buffer */
+	/* Copy the data received into our frame buffer */
 	if (ov511->curframe >= 0) {
-		len = ov511_move_data(ov511, urb);
+		if (ov511->bridge == BRG_OV511 || 
+		    ov511->bridge == BRG_OV511PLUS)
+			len = ov511_move_data(ov511, urb);
+		else if (ov511->bridge == BRG_OV518 ||
+			 ov511->bridge == BRG_OV518PLUS)
+			len = ov518_move_data(ov511, urb);
+		else
+			err("Unknown bridge device (%d)", ov511->bridge);
 	} else if (waitqueue_active(&ov511->wq)) {
 		wake_up_interruptible(&ov511->wq);
 	}
 
-	/* Move to the next sbuf */
-	ov511->cursbuf = (ov511->cursbuf + 1) % OV511_NUMSBUF;
-
 	urb->dev = ov511->dev;
 
 	return;
 }
 
-static int ov511_init_isoc(struct usb_ov511 *ov511)
+/****************************************************************************
+ *
+ * Stream initialization and termination
+ *
+ ***************************************************************************/
+
+static int 
+ov511_init_isoc(struct usb_ov511 *ov511)
 {
 	urb_t *urb;
 	int fx, err, n, size;
 
 	PDEBUG(3, "*** Initializing capture ***");
 
-	ov511->compress = 0;
 	ov511->curframe = -1;
-	ov511->cursbuf = 0;
-	ov511->scratchlen = 0;
 
-	if (ov511->bridge == BRG_OV511)
-		if (cams == 1)			 size = 993;
-		else if (cams == 2)		 size = 513;
-		else if (cams == 3 || cams == 4) size = 257;
+	if (ov511->bridge == BRG_OV511) {
+		if (cams == 1)				size = 993;
+		else if (cams == 2)			size = 513;
+		else if (cams == 3 || cams == 4)	size = 257;
 		else {
 			err("\"cams\" parameter too high!");
 			return -1;
 		}
-	else if (ov511->bridge == BRG_OV511PLUS)
-		if (cams == 1)                      size = 961;
-		else if (cams == 2)                 size = 513;
-		else if (cams == 3 || cams == 4) size = 257;
-		else if (cams >= 5 && cams <= 8)    size = 129;
-		else if (cams >= 9 && cams <= 31)   size = 33;
+	} else if (ov511->bridge == BRG_OV511PLUS) {
+		if (cams == 1)				size = 961;
+		else if (cams == 2)			size = 513;
+		else if (cams == 3 || cams == 4)	size = 257;
+		else if (cams >= 5 && cams <= 8)	size = 129;
+		else if (cams >= 9 && cams <= 31)	size = 33;
 		else {
 			err("\"cams\" parameter too high!");
 			return -1;
 		}
-	else {
+	} else if (ov511->bridge == BRG_OV518 ||
+		   ov511->bridge == BRG_OV518PLUS) {
+		if (cams == 1)				size = 896;
+		else if (cams == 2)			size = 512;
+		else if (cams == 3 || cams == 4)	size = 256;
+		else if (cams >= 5 && cams <= 8)	size = 128;
+		else {
+			err("\"cams\" parameter too high!");
+			return -1;
+		}
+	} else {
 		err("invalid bridge type");
 		return -1;
 	}
 
-	ov511_set_packet_size(ov511, size);
+	if (packetsize == -1) {
+		// FIXME: OV518 is hardcoded to 15 FPS (alternate 5) for now
+		if (ov511->bridge == BRG_OV518 ||
+		    ov511->bridge == BRG_OV518PLUS)
+			ov511_set_packet_size(ov511, 640);
+		else
+			ov511_set_packet_size(ov511, size);
+	} else {
+			info("Forcing packet size to %d", packetsize);
+			ov511_set_packet_size(ov511, packetsize);
+	}
 
 	for (n = 0; n < OV511_NUMSBUF; n++) {
 		urb = usb_alloc_urb(FRAMES_PER_DESC);
@@ -1946,13 +4436,17 @@
 		urb->transfer_buffer = ov511->sbuf[n].data;
 		urb->complete = ov511_isoc_irq;
 		urb->number_of_packets = FRAMES_PER_DESC;
-		urb->transfer_buffer_length = ov511->packet_size * FRAMES_PER_DESC;
+		urb->transfer_buffer_length =
+		 ov511->packet_size * FRAMES_PER_DESC;
 		for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
-			urb->iso_frame_desc[fx].offset = ov511->packet_size * fx;
+			urb->iso_frame_desc[fx].offset = 
+			 ov511->packet_size * fx;
 			urb->iso_frame_desc[fx].length = ov511->packet_size;
 		}
 	}
 
+	ov511->streaming = 1;
+
 	ov511->sbuf[OV511_NUMSBUF - 1].urb->next = ov511->sbuf[0].urb;
 	for (n = 0; n < OV511_NUMSBUF - 1; n++)
 		ov511->sbuf[n].urb->next = ov511->sbuf[n+1].urb;
@@ -1964,19 +4458,18 @@
 			err("init isoc: usb_submit_urb(%d) ret %d", n, err);
 	}
 
-	ov511->streaming = 1;
-
 	return 0;
 }
 
-static void ov511_stop_isoc(struct usb_ov511 *ov511)
+static void 
+ov511_stop_isoc(struct usb_ov511 *ov511)
 {
 	int n;
 
 	if (!ov511->streaming || !ov511->dev)
 		return;
 
-	PDEBUG (3, "*** Stopping capture ***");
+	PDEBUG(3, "*** Stopping capture ***");
 
 	ov511_set_packet_size(ov511, 0);
 
@@ -1993,9 +4486,11 @@
 	}
 }
 
-static int ov511_new_frame(struct usb_ov511 *ov511, int framenum)
+static int 
+ov511_new_frame(struct usb_ov511 *ov511, int framenum)
 {
 	struct ov511_frame *frame;
+	int newnum;
 
 	PDEBUG(4, "ov511->curframe = %d, framenum = %d", ov511->curframe,
 		framenum);
@@ -2005,19 +4500,19 @@
 	/* If we're not grabbing a frame right now and the other frame is */
 	/* ready to be grabbed into, then use it instead */
 	if (ov511->curframe == -1) {
-		if (ov511->frame[(framenum - 1 + OV511_NUMFRAMES) % OV511_NUMFRAMES].grabstate == FRAME_READY)
-			framenum = (framenum - 1 + OV511_NUMFRAMES) % OV511_NUMFRAMES;
+		newnum = (framenum - 1 + OV511_NUMFRAMES) % OV511_NUMFRAMES;
+		if (ov511->frame[newnum].grabstate == FRAME_READY)
+			framenum = newnum;
 	} else
 		return 0;
 
 	frame = &ov511->frame[framenum];
 
-	PDEBUG (4, "framenum = %d, width = %d, height = %d", framenum, 
-		frame->width, frame->height);
+	PDEBUG(4, "framenum = %d, width = %d, height = %d", framenum, 
+	       frame->width, frame->height);
 
 	frame->grabstate = FRAME_GRABBING;
 	frame->scanstate = STATE_SCANNING;
-	frame->scanlength = 0;		/* accumulated in ov511_parse_data() */
 	frame->snapshot = 0;
 
 	ov511->curframe = framenum;
@@ -2041,9 +4536,12 @@
  * Buffer management
  *
  ***************************************************************************/
-static int ov511_alloc(struct usb_ov511 *ov511)
+static int 
+ov511_alloc(struct usb_ov511 *ov511)
 {
 	int i;
+	int w = ov511->maxwidth;
+	int h = ov511->maxheight;
 
 	PDEBUG(4, "entered");
 	down(&ov511->buf_lock);
@@ -2056,15 +4554,29 @@
 	if (ov511->buf_state == BUF_ALLOCATED)
 		goto out;
 
-	ov511->fbuf = rvmalloc(OV511_NUMFRAMES * MAX_DATA_SIZE);
+	ov511->fbuf = rvmalloc(OV511_NUMFRAMES * MAX_DATA_SIZE(w, h));
 	if (!ov511->fbuf)
 		goto error;
 
-	for (i = 0; i < OV511_NUMFRAMES; i++) {
-		ov511->frame[i].grabstate = FRAME_UNUSED;
-		ov511->frame[i].data = ov511->fbuf + i * MAX_DATA_SIZE;
-		PDEBUG(4, "frame[%d] @ %p", i, ov511->frame[i].data);
+	ov511->rawfbuf = vmalloc(OV511_NUMFRAMES * MAX_RAW_DATA_SIZE(w, h));
+	if (!ov511->rawfbuf) {
+		rvfree(ov511->fbuf, OV511_NUMFRAMES * MAX_DATA_SIZE(w, h));
+		ov511->fbuf = NULL;
+		goto error;
+	}
+	memset(ov511->rawfbuf, 0, OV511_NUMFRAMES * MAX_RAW_DATA_SIZE(w, h));
 
+	ov511->tempfbuf = vmalloc(OV511_NUMFRAMES * MAX_RAW_DATA_SIZE(w, h));
+	if (!ov511->tempfbuf) {
+		vfree(ov511->rawfbuf);
+		ov511->rawfbuf = NULL;
+		rvfree(ov511->fbuf, OV511_NUMFRAMES * MAX_DATA_SIZE(w, h));
+		ov511->fbuf = NULL;
+		goto error;
+	}
+	memset(ov511->tempfbuf, 0, OV511_NUMFRAMES * MAX_RAW_DATA_SIZE(w, h));
+
+	for (i = 0; i < OV511_NUMSBUF; i++) {
 		ov511->sbuf[i].data = kmalloc(FRAMES_PER_DESC *
 			MAX_FRAME_SIZE_PER_DESC, GFP_KERNEL);
 		if (!ov511->sbuf[i].data) {
@@ -2072,12 +4584,28 @@
 				kfree(ov511->sbuf[i].data);
 				ov511->sbuf[i].data = NULL;
 			}
-			rvfree(ov511->fbuf, OV511_NUMFRAMES * MAX_DATA_SIZE);
+			vfree(ov511->tempfbuf);
+			ov511->tempfbuf = NULL;
+			vfree(ov511->rawfbuf);
+			ov511->rawfbuf = NULL;
+			rvfree(ov511->fbuf,
+			       OV511_NUMFRAMES * MAX_DATA_SIZE(w, h));
 			ov511->fbuf = NULL;
+
 			goto error;
 		}
 		PDEBUG(4, "sbuf[%d] @ %p", i, ov511->sbuf[i].data);
 	}
+
+	for (i = 0; i < OV511_NUMFRAMES; i++) {
+		ov511->frame[i].data = ov511->fbuf + i * MAX_DATA_SIZE(w, h);
+		ov511->frame[i].rawdata = ov511->rawfbuf 
+		 + i * MAX_RAW_DATA_SIZE(w, h);
+		ov511->frame[i].tempdata = ov511->tempfbuf 
+		 + i * MAX_RAW_DATA_SIZE(w, h);
+		PDEBUG(4, "frame[%d] @ %p", i, ov511->frame[i].data);
+	}
+
 	ov511->buf_state = BUF_ALLOCATED;
 out:
 	up(&ov511->buf_lock);
@@ -2095,29 +4623,48 @@
  * - Because this code will free any non-null pointer, you must be sure to null
  *   them if you explicitly free them somewhere else!
  */
-static void ov511_do_dealloc(struct usb_ov511 *ov511)
+static void 
+ov511_do_dealloc(struct usb_ov511 *ov511)
 {
 	int i;
 	PDEBUG(4, "entered");
 
 	if (ov511->fbuf) {
-		rvfree(ov511->fbuf, OV511_NUMFRAMES * MAX_DATA_SIZE);
+		rvfree(ov511->fbuf, OV511_NUMFRAMES
+		       * MAX_DATA_SIZE(ov511->maxwidth, ov511->maxheight));
 		ov511->fbuf = NULL;
 	}
 
-	for (i = 0; i < OV511_NUMFRAMES; i++) {
+	if (ov511->rawfbuf) {
+		vfree(ov511->rawfbuf);
+		ov511->rawfbuf = NULL;
+	}
+
+	if (ov511->tempfbuf) {
+		vfree(ov511->tempfbuf);
+		ov511->tempfbuf = NULL;
+	}
+
+	for (i = 0; i < OV511_NUMSBUF; i++) {
 		if (ov511->sbuf[i].data) {
 			kfree(ov511->sbuf[i].data);
 			ov511->sbuf[i].data = NULL;
 		}
 	}
 
+	for (i = 0; i < OV511_NUMFRAMES; i++) {
+		ov511->frame[i].data = NULL;
+		ov511->frame[i].rawdata = NULL;
+		ov511->frame[i].tempdata = NULL;
+	}
+
 	PDEBUG(4, "buffer memory deallocated");
 	ov511->buf_state = BUF_NOT_ALLOCATED;
 	PDEBUG(4, "leaving");
 }
 
-static void ov511_buf_callback(unsigned long data)
+static void 
+ov511_buf_callback(unsigned long data)
 {
 	struct usb_ov511 *ov511 = (struct usb_ov511 *)data;
 	PDEBUG(4, "entered");
@@ -2130,7 +4677,8 @@
 	PDEBUG(4, "leaving");
 }
 
-static void ov511_dealloc(struct usb_ov511 *ov511, int now)
+static void 
+ov511_dealloc(struct usb_ov511 *ov511, int now)
 {
 	struct timer_list *bt = &(ov511->buf_timer);
 	PDEBUG(4, "entered");
@@ -2163,13 +4711,14 @@
  *
  ***************************************************************************/
 
-static int ov511_open(struct video_device *dev, int flags)
+static int 
+ov511_open(struct video_device *vdev, int flags)
 {
-	struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
-	int err;
+	struct usb_ov511 *ov511 = vdev->priv;
+	int err, i;
 
-	MOD_INC_USE_COUNT;
 	PDEBUG(4, "opening");
+
 	down(&ov511->lock);
 
 	err = -EBUSY;
@@ -2182,6 +4731,24 @@
 
 	ov511->sub_flag = 0;
 
+	/* In case app doesn't set them... */
+	if (ov51x_set_default_params(ov511) < 0)
+		goto out;
+
+	/* Make sure frames are reset */
+	for (i = 0; i < OV511_NUMFRAMES; i++) {
+		ov511->frame[i].grabstate = FRAME_UNUSED;
+		ov511->frame[i].bytes_read = 0;
+	}
+
+	/* If compression is on, make sure now that a 
+	 * decompressor can be loaded */
+	if (ov511->compress && !ov511->decomp_ops) {
+		err = ov51x_request_decompressor(ov511);
+		if (err)
+			goto out;
+	}
+
 	err = ov511_init_isoc(ov511);
 	if (err) {
 		ov511_dealloc(ov511, 0);
@@ -2189,17 +4756,18 @@
 	}
 
 	ov511->user++;
+	
+	if (ov511->led_policy == LED_AUTO)
+		ov51x_led_control(ov511, 1);
 
 out:
 	up(&ov511->lock);
 
-	if (err)
-		MOD_DEC_USE_COUNT;
-
 	return err;
 }
 
-static void ov511_close(struct video_device *dev)
+static void 
+ov511_close(struct video_device *dev)
 {
 	struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
 
@@ -2210,40 +4778,50 @@
 	ov511->user--;
 	ov511_stop_isoc(ov511);
 
+	ov51x_release_decompressor(ov511);
+
+	if (ov511->led_policy == LED_AUTO)
+		ov51x_led_control(ov511, 0);
+
 	if (ov511->dev)
 		ov511_dealloc(ov511, 0);
 
 	up(&ov511->lock);
 
+	/* Device unplugged while open. Only a minimum of unregistration is done
+	 * here; the disconnect callback already did the rest. */
 	if (!ov511->dev) {
 		ov511_dealloc(ov511, 1);
 		video_unregister_device(&ov511->vdev);
 		kfree(ov511);
 		ov511 = NULL;
 	}
-
-	MOD_DEC_USE_COUNT;
 }
 
-static int ov511_init_done(struct video_device *dev)
+static int 
+ov511_init_done(struct video_device *vdev)
 {
 #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
-	create_proc_ov511_cam((struct usb_ov511 *)dev);
+	create_proc_ov511_cam((struct usb_ov511 *)vdev);
 #endif
 
 	return 0;
 }
 
-static long ov511_write(struct video_device *dev, const char *buf, unsigned long count, int noblock)
+static long 
+ov511_write(struct video_device *vdev, const char *buf,
+	    unsigned long count, int noblock)
 {
 	return -EINVAL;
 }
 
-static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
+/* Do not call this function directly! */
+static int 
+ov511_ioctl_internal(struct video_device *vdev, unsigned int cmd, void *arg)
 {
 	struct usb_ov511 *ov511 = (struct usb_ov511 *)vdev;
 
-	PDEBUG(4, "IOCtl: 0x%X", cmd);
+	PDEBUG(5, "IOCtl: 0x%X", cmd);
 
 	if (!ov511->dev)
 		return -EIO;	
@@ -2253,17 +4831,24 @@
 	{
 		struct video_capability b;
 
-		PDEBUG (4, "VIDIOCGCAP");
+		PDEBUG(4, "VIDIOCGCAP");
 
 		memset(&b, 0, sizeof(b));
-		strcpy(b.name, "OV511 USB Camera");
+		sprintf(b.name, "%s USB Camera",
+			ov511->bridge == BRG_OV511 ? "OV511" :
+			ov511->bridge == BRG_OV511PLUS ? "OV511+" :
+			ov511->bridge == BRG_OV518 ? "OV518" :
+			ov511->bridge == BRG_OV518PLUS ? "OV518+" :
+			"unknown");
 		b.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE;
-		b.channels = 1;
-		b.audios = 0;
+		if (ov511->has_tuner)
+			b.type |= VID_TYPE_TUNER;
+		b.channels = ov511->num_inputs;
+		b.audios = ov511->has_audio_proc ? 1:0;
 		b.maxwidth = ov511->maxwidth;
 		b.maxheight = ov511->maxheight;
-		b.minwidth = 160;
-		b.minheight = 120;
+		b.minwidth = ov511->minwidth;
+		b.minheight = ov511->minheight;
 
 		if (copy_to_user(arg, &b, sizeof(b)))
 			return -EFAULT;
@@ -2274,15 +4859,23 @@
 	{
 		struct video_channel v;
 
+		PDEBUG(4, "VIDIOCGCHAN");
+
 		if (copy_from_user(&v, arg, sizeof(v)))
 			return -EFAULT;
-		if (v.channel != 0)
+
+		if ((unsigned)(v.channel) >= ov511->num_inputs) {
+			err("Invalid channel (%d)", v.channel);
 			return -EINVAL;
+		}
 
-		v.flags = 0;
-		v.tuners = 0;
-		v.type = VIDEO_TYPE_CAMERA;
-		strcpy(v.name, "Camera");
+		v.norm = ov511->norm;
+		v.type = (ov511->has_tuner) ? VIDEO_TYPE_TV : VIDEO_TYPE_CAMERA;
+		v.flags = (ov511->has_tuner) ? VIDEO_VC_TUNER : 0;
+		v.flags |= (ov511->has_audio_proc) ? VIDEO_VC_AUDIO : 0;
+//		v.flags |= (ov511->has_decoder) ? VIDEO_VC_NORM : 0;
+		v.tuners = (ov511->has_tuner) ? 1:0;
+		decoder_get_input_name(ov511, v.channel, v.name);
 
 		if (copy_to_user(arg, &v, sizeof(v)))
 			return -EFAULT;
@@ -2291,13 +4884,42 @@
 	}
 	case VIDIOCSCHAN:
 	{
-		int v;
+		struct video_channel v;
+		int err;
+
+		PDEBUG(4, "VIDIOCSCHAN");
 
 		if (copy_from_user(&v, arg, sizeof(v)))
 			return -EFAULT;
 
-		if (v != 0)
+		/* Make sure it's not a camera */
+		if (!ov511->has_decoder) {
+			if (v.channel == 0)
+				return 0;
+			else
+				return -EINVAL;
+		}
+
+		if (v.norm != VIDEO_MODE_PAL &&
+		    v.norm != VIDEO_MODE_NTSC &&
+		    v.norm != VIDEO_MODE_SECAM &&
+		    v.norm != VIDEO_MODE_AUTO) {
+			err("Invalid norm (%d)", v.norm);
+			return -EINVAL;
+		}
+
+		if ((unsigned)(v.channel) >= ov511->num_inputs) {
+			err("Invalid channel (%d)", v.channel);
 			return -EINVAL;
+		}
+
+		err = decoder_set_input(ov511, v.channel);
+		if (err)
+			return err;
+
+		err = decoder_set_norm(ov511, v.norm);
+		if (err)
+			return err;
 
 		return 0;
 	}
@@ -2305,11 +4927,11 @@
 	{
 		struct video_picture p;
 
-		PDEBUG (4, "VIDIOCGPICT");
+		PDEBUG(4, "VIDIOCGPICT");
 
 		memset(&p, 0, sizeof(p));
 
-		if (ov7610_get_picture(ov511, &p))
+		if (sensor_get_picture(ov511, &p))
 			return -EIO;
 
 		if (copy_to_user(arg, &p, sizeof(p)))
@@ -2322,22 +4944,40 @@
 		struct video_picture p;
 		int i;
 
-		PDEBUG (4, "VIDIOCSPICT");
+		PDEBUG(4, "VIDIOCSPICT");
 
 		if (copy_from_user(&p, arg, sizeof(p)))
 			return -EFAULT;
 
 		if (!ov511_get_depth(p.palette))
 			return -EINVAL;
-			
-		if (ov7610_set_picture(ov511, &p))
+
+		if (sensor_set_picture(ov511, &p))
 			return -EIO;
 
+		if (force_palette && p.palette != force_palette) {
+			info("Palette rejected (%d)", p.palette);
+			return -EINVAL;
+		}
+
+		// FIXME: Format should be independent of frames
+		if (p.palette != ov511->frame[0].format) {
+			PDEBUG(4, "Detected format change");
+
+			/* If we're collecting previous frame wait
+			   before changing modes */
+			interruptible_sleep_on(&ov511->wq);
+			if (signal_pending(current)) return -EINTR;
+
+			mode_init_regs(ov511, ov511->frame[0].width,
+				ov511->frame[0].height,	p.palette,
+				ov511->sub_flag);
+		}
+
 		PDEBUG(4, "Setting depth=%d, palette=%d", p.depth, p.palette);
 		for (i = 0; i < OV511_NUMFRAMES; i++) {
 			ov511->frame[i].depth = p.depth;
 			ov511->frame[i].format = p.palette;
-			ov511->frame[i].segsize = GET_SEGSIZE(p.palette);
 		}
 
 		return 0;
@@ -2346,7 +4986,7 @@
 	{
 		int vf;
 
-		PDEBUG (4, "VIDIOCGCAPTURE");
+		PDEBUG(4, "VIDIOCGCAPTURE");
 
 		if (copy_from_user(&vf, arg, sizeof(vf)))
 			return -EFAULT;
@@ -2357,6 +4997,8 @@
 	{
 		struct video_capture vc;
 
+		PDEBUG(4, "VIDIOCSCAPTURE");
+
 		if (copy_from_user(&vc, arg, sizeof(vc)))
 			return -EFAULT;
 		if (vc.flags)
@@ -2391,7 +5033,7 @@
 		if (copy_from_user(&vw, arg, sizeof(vw)))
 			return -EFAULT;
 
-		PDEBUG (4, "VIDIOCSWIN: width=%d, height=%d",
+		PDEBUG(4, "VIDIOCSWIN: width=%d, height=%d",
 			vw.width, vw.height);
 
 #if 0
@@ -2410,7 +5052,7 @@
 		interruptible_sleep_on(&ov511->wq);
 		if (signal_pending(current)) return -EINTR;
 
-		result = ov511_mode_init_regs(ov511, vw.width, vw.height,
+		result = mode_init_regs(ov511, vw.width, vw.height,
 			ov511->frame[0].format, ov511->sub_flag);
 		if (result < 0)
 			return result;
@@ -2433,7 +5075,7 @@
 		vw.height = ov511->frame[0].height;
 		vw.flags = 30;
 
-		PDEBUG (4, "VIDIOCGWIN: %dx%d", vw.width, vw.height);
+		PDEBUG(4, "VIDIOCGWIN: %dx%d", vw.width, vw.height);
 
 		if (copy_to_user(arg, &vw, sizeof(vw)))
 			return -EFAULT;
@@ -2444,14 +5086,18 @@
 	{
 		struct video_mbuf vm;
 		int i;
-		
+
+		PDEBUG(4, "VIDIOCGMBUF");
+
 		memset(&vm, 0, sizeof(vm));
-		vm.size = OV511_NUMFRAMES * MAX_DATA_SIZE;
+		vm.size = OV511_NUMFRAMES
+			* MAX_DATA_SIZE(ov511->maxwidth, ov511->maxheight);
 		vm.frames = OV511_NUMFRAMES;
+
 		vm.offsets[0] = 0;
 		for (i = 1; i < OV511_NUMFRAMES; i++) {
-			vm.offsets[i] = vm.offsets[i-1] + MAX_FRAME_SIZE
-				+ sizeof (struct timeval);
+			vm.offsets[i] = vm.offsets[i-1]
+			   + MAX_DATA_SIZE(ov511->maxwidth, ov511->maxheight);
 		}
 
 		if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
@@ -2482,63 +5128,74 @@
 			return -EINVAL;
 		}
 
-		if (vm.width > ov511->maxwidth || vm.height > ov511->maxheight) {
+		if (vm.width > ov511->maxwidth 
+		    || vm.height > ov511->maxheight) {
 			err("VIDIOCMCAPTURE: requested dimensions too big");
 			return -EINVAL;
 		}
 
-		if (ov511->frame[vm.frame].grabstate == FRAME_GRABBING)
+		if (ov511->frame[vm.frame].grabstate == FRAME_GRABBING) {
+			PDEBUG(4, "VIDIOCMCAPTURE: already grabbing");
 			return -EBUSY;
+		}
+
+		if (force_palette && vm.format != force_palette) {
+			info("palette rejected (%d)", vm.format);
+			return -EINVAL;
+		}
 
-		/* Don't compress if the size changed */
 		if ((ov511->frame[vm.frame].width != vm.width) ||
 		    (ov511->frame[vm.frame].height != vm.height) ||
 		    (ov511->frame[vm.frame].format != vm.format) ||
-		    (ov511->frame[vm.frame].sub_flag !=
-		     ov511->sub_flag)) {
+		    (ov511->frame[vm.frame].sub_flag != ov511->sub_flag) ||
+		    (ov511->frame[vm.frame].depth != depth)) {
+			PDEBUG(4, "VIDIOCMCAPTURE: change in image parameters");
+
 			/* If we're collecting previous frame wait
 			   before changing modes */
 			interruptible_sleep_on(&ov511->wq);
 			if (signal_pending(current)) return -EINTR;
-			ret = ov511_mode_init_regs(ov511, vm.width, vm.height,
+			ret = mode_init_regs(ov511, vm.width, vm.height,
 				vm.format, ov511->sub_flag);
 #if 0
-			if (ret < 0)
+			if (ret < 0) {
+				PDEBUG(1, "Got error while initializing regs ");
 				return ret;
+			}
 #endif
+			ov511->frame[vm.frame].width = vm.width;
+			ov511->frame[vm.frame].height = vm.height;
+			ov511->frame[vm.frame].format = vm.format;
+			ov511->frame[vm.frame].sub_flag = ov511->sub_flag;
+			ov511->frame[vm.frame].depth = depth;
 		}
 
-		ov511->frame[vm.frame].width = vm.width;
-		ov511->frame[vm.frame].height = vm.height;
-		ov511->frame[vm.frame].format = vm.format;
-		ov511->frame[vm.frame].sub_flag = ov511->sub_flag;
-		ov511->frame[vm.frame].segsize = GET_SEGSIZE(vm.format);
-		ov511->frame[vm.frame].depth = depth;
-
 		/* Mark it as ready */
 		ov511->frame[vm.frame].grabstate = FRAME_READY;
 
+		PDEBUG(4, "VIDIOCMCAPTURE: renewing frame %d", vm.frame);
+
 		return ov511_new_frame(ov511, vm.frame);
 	}
 	case VIDIOCSYNC:
 	{
-		int frame;
+		int fnum, rc;
+		struct ov511_frame *frame;
 
-		if (copy_from_user((void *)&frame, arg, sizeof(int)))
+		if (copy_from_user((void *)&fnum, arg, sizeof(int)))
 			return -EFAULT;
 
-		if ((unsigned)frame >= OV511_NUMFRAMES) {
-			err("VIDIOCSYNC: invalid frame (%d)", frame);
+		if ((unsigned)fnum >= OV511_NUMFRAMES) {
+			err("VIDIOCSYNC: invalid frame (%d)", fnum);
 			return -EINVAL;
 		}
 
-		PDEBUG(4, "syncing to frame %d, grabstate = %d", frame,
-		       ov511->frame[frame].grabstate);
+		frame = &ov511->frame[fnum];
 
-		if(frame < 0 || frame >= OV511_NUMFRAMES)
-			return -EINVAL;
-			
-		switch (ov511->frame[frame].grabstate) {
+		PDEBUG(4, "syncing to frame %d, grabstate = %d", fnum,
+		       frame->grabstate);
+
+		switch (frame->grabstate) {
 		case FRAME_UNUSED:
 			return -EINVAL;
 		case FRAME_READY:
@@ -2548,56 +5205,41 @@
 			if (!ov511->dev)
 				return -EIO;
 
-			do {
-#if 0
-				init_waitqueue_head(&ov511->frame[frame].wq);
-#endif
-				interruptible_sleep_on(&ov511->frame[frame].wq);
-				if (signal_pending(current)) {
-					if (retry_sync) {
-						PDEBUG(3, "***retry sync***");
-
-						/* Polling apps will destroy frames with that! */
-						ov511_new_frame(ov511, frame);
-						ov511->curframe = -1;
-
-						/* This will request another frame. */
-						if (waitqueue_active(&ov511->frame[frame].wq))
-							wake_up_interruptible(&ov511->frame[frame].wq);
-
-						return 0;
- 					} else {
-						return -EINTR;
-					}
-				}
-			} while (ov511->frame[frame].grabstate == FRAME_GRABBING);
+			rc = wait_event_interruptible(frame->wq,
+			    (frame->grabstate == FRAME_DONE)
+			    || (frame->grabstate == FRAME_ERROR));
+
+			if (rc)
+				return rc;
 
-			if (ov511->frame[frame].grabstate == FRAME_ERROR) {
+			if (frame->grabstate == FRAME_ERROR) {
 				int ret;
 
-				if ((ret = ov511_new_frame(ov511, frame)) < 0)
+				if ((ret = ov511_new_frame(ov511, fnum)) < 0)
 					return ret;
 				goto redo;
-			}			
+			}
+			/* Fall through */			
 		case FRAME_DONE:
-			if (ov511->snap_enabled && !ov511->frame[frame].snapshot) {
+			if (ov511->snap_enabled && !frame->snapshot) {
 				int ret;
-				if ((ret = ov511_new_frame(ov511, frame)) < 0)
+				if ((ret = ov511_new_frame(ov511, fnum)) < 0)
 					return ret;
 				goto redo;
 			}
 
-			ov511->frame[frame].grabstate = FRAME_UNUSED;
+			frame->grabstate = FRAME_UNUSED;
 
 			/* Reset the hardware snapshot button */
 			/* FIXME - Is this the best place for this? */
-			if ((ov511->snap_enabled) &&
-			    (ov511->frame[frame].snapshot)) {
-				ov511->frame[frame].snapshot = 0;
-				ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01);
-				ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x03);
-				ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01);
+			if ((ov511->snap_enabled) && (frame->snapshot)) {
+				frame->snapshot = 0;
+				ov51x_clear_snapshot(ov511);
 			}
+
+			/* Decompression, format conversion, etc... */
+			ov511_postprocess(ov511, frame);
+
 			break;
 		} /* end switch */
 
@@ -2607,6 +5249,8 @@
 	{
 		struct video_buffer vb;
 
+		PDEBUG(4, "VIDIOCSCHAN");
+
 		memset(&vb, 0, sizeof(vb));
 		vb.base = NULL;	/* frame buffer not supported, not used */
 
@@ -2615,43 +5259,170 @@
 
 		return 0;
 	}
-	case VIDIOCKEY:
+	case VIDIOCGUNIT:
+	{
+		struct video_unit vu;
+
+		PDEBUG(4, "VIDIOCGUNIT");
+
+		memset(&vu, 0, sizeof(vu));
+
+		vu.video = ov511->vdev.minor;	/* Video minor */
+		vu.vbi = VIDEO_NO_UNIT;		/* VBI minor */
+		vu.radio = VIDEO_NO_UNIT;	/* Radio minor */
+		vu.audio = VIDEO_NO_UNIT;	/* Audio minor */
+		vu.teletext = VIDEO_NO_UNIT;	/* Teletext minor */
+
+		if (copy_to_user((void *)arg, (void *)&vu, sizeof(vu)))
+			return -EFAULT;
+
 		return 0;
-	case VIDIOCCAPTURE:
-		return -EINVAL;
-	case VIDIOCSFBUF:
-		return -EINVAL;
+	}
 	case VIDIOCGTUNER:
+	{
+		struct video_tuner v;
+
+		PDEBUG(4, "VIDIOCGTUNER");
+
+		if (copy_from_user(&v, arg, sizeof(v)))
+			return -EFAULT;
+
+		if (!ov511->has_tuner || v.tuner)	// Only tuner 0
+			return -EINVAL;
+
+		strcpy(v.name, "Television");
+
+		// FIXME: Need a way to get the real values
+		v.rangelow = 0;
+		v.rangehigh = ~0;
+
+		v.flags = VIDEO_TUNER_PAL | VIDEO_TUNER_NTSC |
+		    VIDEO_TUNER_SECAM;
+		v.mode = 0; 		/* FIXME:  Not sure what this is yet */
+		v.signal = 0xFFFF;	/* unknown */
+
+		call_i2c_clients(ov511, cmd, &v);
+
+		if (copy_to_user(arg, &v, sizeof(v)))
+			return -EFAULT;
+
+		return 0;
+	}
 	case VIDIOCSTUNER:
-		return -EINVAL;
+	{
+		struct video_tuner v;
+		int err;
+
+		PDEBUG(4, "VIDIOCSTUNER");
+
+		if (copy_from_user(&v, arg, sizeof(v)))
+			return -EFAULT;
+
+		/* Only no or one tuner for now */
+		if (!ov511->has_tuner || v.tuner)
+			return -EINVAL;
+
+		/* and it only has certain valid modes */
+		if (v.mode != VIDEO_MODE_PAL &&
+		    v.mode != VIDEO_MODE_NTSC &&
+		    v.mode != VIDEO_MODE_SECAM) return -EOPNOTSUPP;
+
+		/* Is this right/necessary? */
+		err = decoder_set_norm(ov511, v.mode);
+		if (err)
+			return err;
+
+		call_i2c_clients(ov511, cmd, &v);
+
+		return 0;
+	}
 	case VIDIOCGFREQ:
+	{
+		unsigned long v = ov511->freq;
+
+		PDEBUG(4, "VIDIOCGFREQ");
+
+		if (!ov511->has_tuner)
+			return -EINVAL;
+#if 0
+		/* FIXME: this is necessary for testing */
+		v = 46*16;
+#endif
+		if (copy_to_user(arg, &v, sizeof(v)))
+			return -EFAULT;
+
+		return 0;
+	}
 	case VIDIOCSFREQ:
-		return -EINVAL;
+	{
+		unsigned long v;
+
+		if (!ov511->has_tuner)
+			return -EINVAL;
+
+		if (copy_from_user(&v, arg, sizeof(v)))
+			return -EFAULT;
+
+		PDEBUG(4, "VIDIOCSFREQ: %lx", v);
+
+		ov511->freq = v;
+		call_i2c_clients(ov511, cmd, &v);
+
+		return 0;
+	}
 	case VIDIOCGAUDIO:
 	case VIDIOCSAUDIO:
-		return -EINVAL;
+	{
+		/* FIXME: Implement this... */
+		return 0;
+	}
 	default:
+		PDEBUG(3, "Unsupported IOCtl: 0x%X", cmd);
 		return -ENOIOCTLCMD;
 	} /* end switch */
 
 	return 0;
 }
 
-static long ov511_read(struct video_device *dev, char *buf, unsigned long count, int noblock)
+static int 
+ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
 {
-	struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
-	int i;
-	int frmx = -1;
-	volatile struct ov511_frame *frame;
+	int rc;
+	struct usb_ov511 *ov511 = vdev->priv;
+
+	if (down_interruptible(&ov511->lock))
+		return -EINTR;
+
+	rc = ov511_ioctl_internal(vdev, cmd, arg);
+
+	up(&ov511->lock);
+	return rc;
+}
+
+static inline long 
+ov511_read(struct video_device *vdev, char *buf, unsigned long count,
+	   int noblock)
+{
+	struct usb_ov511 *ov511 = vdev->priv;
+	int i, rc = 0, frmx = -1;
+	struct ov511_frame *frame;
+
+	if (down_interruptible(&ov511->lock))
+		return -EINTR;
 
 	PDEBUG(4, "%ld bytes, noblock=%d", count, noblock);
 
-	if (!dev || !buf)
-		return -EFAULT;
+	if (!vdev || !buf) {
+		rc = -EFAULT;
+		goto error;
+	}
 
-	if (!ov511->dev)
-		return -EIO;
+	if (!ov511->dev) {
+		rc = -EIO;
+		goto error;
+	}
 
+// FIXME: Only supports two frames
 	/* See if a frame is completed, then use it. */
 	if (ov511->frame[0].grabstate >= FRAME_DONE)	/* _DONE or _ERROR */
 		frmx = 0;
@@ -2659,8 +5430,10 @@
 		frmx = 1;
 
 	/* If nonblocking we return immediately */
-	if (noblock && (frmx == -1))
-		return -EAGAIN;
+	if (noblock && (frmx == -1)) {
+		rc = -EAGAIN;
+		goto error;
+	}
 
 	/* If no FRAME_DONE, look for a FRAME_GRABBING state. */
 	/* See if a frame is in process (grabbing), then use it. */
@@ -2672,89 +5445,116 @@
 	}
 
 	/* If no frame is active, start one. */
-	if (frmx == -1)
-		ov511_new_frame(ov511, frmx = 0);
+	if (frmx == -1) {
+		if ((rc = ov511_new_frame(ov511, frmx = 0))) {
+			err("read: ov511_new_frame error");
+			goto error;
+		}
+	}
 
 	frame = &ov511->frame[frmx];
 
 restart:
-	if (!ov511->dev)
-		return -EIO;
+	if (!ov511->dev) {
+		rc = -EIO;
+		goto error;
+	}
 
 	/* Wait while we're grabbing the image */
 	PDEBUG(4, "Waiting image grabbing");
-	while (frame->grabstate == FRAME_GRABBING) {
-		interruptible_sleep_on(&ov511->frame[frmx].wq);
-		if (signal_pending(current))
-			return -EINTR;
-	}
+	rc = wait_event_interruptible(frame->wq, 
+		(frame->grabstate == FRAME_DONE)
+		|| (frame->grabstate == FRAME_ERROR));
+
+	if (rc)
+		goto error;
+
 	PDEBUG(4, "Got image, frame->grabstate = %d", frame->grabstate);
+	PDEBUG(4, "bytes_recvd = %d", frame->bytes_recvd);
 
 	if (frame->grabstate == FRAME_ERROR) {
 		frame->bytes_read = 0;
 		err("** ick! ** Errored frame %d", ov511->curframe);
-		if (ov511_new_frame(ov511, frmx))
+		if (ov511_new_frame(ov511, frmx)) {
 			err("read: ov511_new_frame error");
+			goto error;
+		}
 		goto restart;
 	}
 
 
 	/* Repeat until we get a snapshot frame */
 	if (ov511->snap_enabled)
-		PDEBUG (4, "Waiting snapshot frame");
+		PDEBUG(4, "Waiting snapshot frame");
 	if (ov511->snap_enabled && !frame->snapshot) {
 		frame->bytes_read = 0;
-		if (ov511_new_frame(ov511, frmx))
-			err("ov511_new_frame error");
+		if ((rc = ov511_new_frame(ov511, frmx))) {
+			err("read: ov511_new_frame error");
+			goto error;
+		}
 		goto restart;
 	}
 
 	/* Clear the snapshot */
 	if (ov511->snap_enabled && frame->snapshot) {
 		frame->snapshot = 0;
-		ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01);
-		ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x03);
-		ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01);
+		ov51x_clear_snapshot(ov511);
 	}
 
-	PDEBUG(4, "frmx=%d, bytes_read=%ld, scanlength=%ld", frmx,
-		frame->bytes_read, frame->scanlength);
+	/* Decompression, format conversion, etc... */
+	ov511_postprocess(ov511, frame);
+
+	PDEBUG(4, "frmx=%d, bytes_read=%ld, length=%ld", frmx,
+		frame->bytes_read,
+		get_frame_length(frame));
 
 	/* copy bytes to user space; we allow for partials reads */
-//	if ((count + frame->bytes_read) > frame->scanlength)
+//	if ((count + frame->bytes_read) 
+//	    > get_frame_length((struct ov511_frame *)frame))
 //		count = frame->scanlength - frame->bytes_read;
 
 	/* FIXME - count hardwired to be one frame... */
-	count = frame->width * frame->height * (frame->depth >> 3);
+	count = get_frame_length(frame);
 
 	PDEBUG(4, "Copy to user space: %ld bytes", count);
 	if ((i = copy_to_user(buf, frame->data + frame->bytes_read, count))) {
 		PDEBUG(4, "Copy failed! %d bytes not copied", i);
-		return -EFAULT;
+		rc = -EFAULT;
+		goto error;
 	}
 
 	frame->bytes_read += count;
 	PDEBUG(4, "{copy} count used=%ld, new bytes_read=%ld",
 		count, frame->bytes_read);
 
-	if (frame->bytes_read >= frame->scanlength) { /* All data has been read */
+	/* If all data has been read... */
+	if (frame->bytes_read
+	    >= get_frame_length(frame)) {
 		frame->bytes_read = 0;
 
+// FIXME: Only supports two frames
 		/* Mark it as available to be used again. */
 		ov511->frame[frmx].grabstate = FRAME_UNUSED;
-		if (ov511_new_frame(ov511, !frmx))
+		if ((rc = ov511_new_frame(ov511, !frmx))) {
 			err("ov511_new_frame returned error");
+			goto error;
+		}
 	}
 
 	PDEBUG(4, "read finished, returning %ld (sweet)", count);
 
+	up(&ov511->lock);
 	return count;
+
+error:
+	up(&ov511->lock);
+	return rc;
 }
 
-static int ov511_mmap(struct video_device *dev, const char *adr,
-	unsigned long size)
+static int 
+ov511_mmap(struct video_device *vdev, const char *adr, unsigned long size)
 {
-	struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
+	struct usb_ov511 *ov511 = vdev->priv;
 	unsigned long start = (unsigned long)adr;
 	unsigned long page, pos;
 
@@ -2763,14 +5563,21 @@
 
 	PDEBUG(4, "mmap: %ld (%lX) bytes", size, size);
 
-	if (size > (((OV511_NUMFRAMES * MAX_DATA_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))
+	if (size > (((OV511_NUMFRAMES
+	              * MAX_DATA_SIZE(ov511->maxwidth, ov511->maxheight)
+	              + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))))
 		return -EINVAL;
 
+	if (down_interruptible(&ov511->lock))
+		return -EINTR;
+
 	pos = (unsigned long)ov511->fbuf;
 	while (size > 0) {
 		page = kvirt_to_pa(pos);
-		if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED))
+		if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) {
+			up(&ov511->lock);
 			return -EAGAIN;
+		}
 		start += PAGE_SIZE;
 		pos += PAGE_SIZE;
 		if (size > PAGE_SIZE)
@@ -2779,10 +5586,12 @@
 			size = 0;
 	}
 
+	up(&ov511->lock);
 	return 0;
 }
 
 static struct video_device ov511_template = {
+	owner:		THIS_MODULE,
 	name:		"OV511 USB Camera",
 	type:		VID_TYPE_CAPTURE,
 	hardware:	VID_HARDWARE_OV511,
@@ -2795,15 +5604,237 @@
 	initialize:	ov511_init_done,
 };
 
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+static int 
+ov511_control_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+		    unsigned long ularg)
+{
+	struct proc_dir_entry *pde;
+	struct usb_ov511 *ov511;
+	void *arg = (void *) ularg;
+	int rc;
+
+	pde = (struct proc_dir_entry *) inode->u.generic_ip;
+	if (!pde)
+		return -ENOENT;
+
+	ov511 = (struct usb_ov511 *) pde->data;
+	if (!ov511)
+		return -ENODEV;
+
+	if (!ov511->dev)
+		return -EIO;
+
+	/* Should we pass through standard V4L IOCTLs? */
+
+	switch (cmd) {
+	case OV511IOC_GINTVER:
+	{
+		int ver = OV511_INTERFACE_VER;
+
+		PDEBUG(4, "Get interface version: %d", ver);
+		if (copy_to_user(arg, &ver, sizeof(ver)))
+			return -EFAULT;
+
+		return 0;
+	}
+	case OV511IOC_GUSHORT:
+	{
+		struct ov511_ushort_opt opt;
+
+		if (copy_from_user(&opt, arg, sizeof(opt)))
+			return -EFAULT;
+
+		switch (opt.optnum) {
+		case OV511_USOPT_BRIGHT:
+			rc = sensor_get_brightness(ov511, &(opt.val));
+			if (rc)	return rc;
+			break;
+		case OV511_USOPT_SAT:
+			rc = sensor_get_saturation(ov511, &(opt.val));
+			if (rc)	return rc;
+			break;
+		case OV511_USOPT_HUE:
+			rc = sensor_get_hue(ov511, &(opt.val));
+			if (rc)	return rc;
+			break;
+		case OV511_USOPT_CONTRAST:
+			rc = sensor_get_contrast(ov511, &(opt.val));
+			if (rc)	return rc;
+			break;
+		default:
+			err("Invalid get short option number");
+			return -EINVAL;
+		}
+
+		if (copy_to_user(arg, &opt, sizeof(opt)))
+			return -EFAULT;
+
+		return 0;
+	}
+	case OV511IOC_SUSHORT:
+	{
+		struct ov511_ushort_opt opt;
+
+		if (copy_from_user(&opt, arg, sizeof(opt)))
+			return -EFAULT;
+
+		switch (opt.optnum) {
+		case OV511_USOPT_BRIGHT:
+			rc = sensor_set_brightness(ov511, opt.val);
+			if (rc)	return rc;
+			break;
+		case OV511_USOPT_SAT:
+			rc = sensor_set_saturation(ov511, opt.val);
+			if (rc)	return rc;
+			break;
+		case OV511_USOPT_HUE:
+			rc = sensor_set_hue(ov511, opt.val);
+			if (rc)	return rc;
+			break;
+		case OV511_USOPT_CONTRAST:
+			rc = sensor_set_contrast(ov511, opt.val);
+			if (rc)	return rc;
+			break;
+		default:
+			err("Invalid set short option number");
+			return -EINVAL;
+		}
+
+		return 0;
+	}
+	case OV511IOC_GUINT:
+	{
+		struct ov511_uint_opt opt;
+
+		if (copy_from_user(&opt, arg, sizeof(opt)))
+			return -EFAULT;
+
+		switch (opt.optnum) {
+		case OV511_UIOPT_POWER_FREQ:
+			opt.val = ov511->lightfreq;
+			break;
+		case OV511_UIOPT_BFILTER:
+			opt.val = ov511->bandfilt;
+			break;
+		case OV511_UIOPT_LED:
+			opt.val = ov511->led_policy;
+			break;
+		case OV511_UIOPT_DEBUG:
+			opt.val = debug;
+			break;
+		case OV511_UIOPT_COMPRESS:
+			opt.val = ov511->compress;
+			break;
+		default:
+			err("Invalid get int option number");
+			return -EINVAL;
+		}
+
+		if (copy_to_user(arg, &opt, sizeof(opt)))
+			return -EFAULT;
+
+		return 0;
+	}
+	case OV511IOC_SUINT:
+	{
+		struct ov511_uint_opt opt;
+
+		if (copy_from_user(&opt, arg, sizeof(opt)))
+			return -EFAULT;
+
+		switch (opt.optnum) {
+		case OV511_UIOPT_POWER_FREQ:
+			rc = sensor_set_light_freq(ov511, opt.val);
+			if (rc)	return rc;
+			break;
+		case OV511_UIOPT_BFILTER:
+			rc = sensor_set_banding_filter(ov511, opt.val);
+			if (rc)	return rc;
+			break;
+		case OV511_UIOPT_LED:
+			if (opt.val <= 2) {
+				ov511->led_policy = opt.val;
+				if (ov511->led_policy == LED_OFF)
+					ov51x_led_control(ov511, 0);
+				else if (ov511->led_policy == LED_ON)
+					ov51x_led_control(ov511, 1);
+			} else {
+				return -EINVAL;
+			}
+			break;
+		case OV511_UIOPT_DEBUG:
+			if (opt.val <= 5)
+				debug = opt.val;
+			else
+				return -EINVAL;
+			break;
+		case OV511_UIOPT_COMPRESS:
+			ov511->compress = opt.val;
+			if (ov511->compress) {
+				if (ov511->bridge == BRG_OV511 ||
+				    ov511->bridge == BRG_OV511PLUS)
+					ov511_init_compression(ov511);
+				else if (ov511->bridge == BRG_OV518 ||
+					 ov511->bridge == BRG_OV518PLUS)
+					ov518_init_compression(ov511);
+			}
+			break;
+		default:
+			err("Invalid get int option number");
+			return -EINVAL;
+		}
+
+		return 0;
+	}
+	case OV511IOC_WI2C:
+	{
+		struct ov511_i2c_struct w;
+
+		if (copy_from_user(&w, arg, sizeof(w)))
+			return -EFAULT;
+
+		return ov51x_i2c_write_slave(ov511, w.slave, w.reg, w.value,
+			w.mask);
+	}
+	case OV511IOC_RI2C:
+	{
+		struct ov511_i2c_struct r;
+
+		if (copy_from_user(&r, arg, sizeof(r)))
+			return -EFAULT;
+
+		rc = ov51x_i2c_read_slave(ov511, r.slave, r.reg);
+		if (rc < 0)
+			return rc;
+
+		r.value = rc;
+
+		if (copy_to_user(arg, &r, sizeof(r)))
+			return -EFAULT;
+
+		return 0;
+	}
+	default:
+		return -EINVAL;
+	} /* end switch */
+
+	return 0;
+}
+#endif
+
 /****************************************************************************
  *
- * OV511/OV7610 configuration
+ * OV511 and sensor configuration
  *
  ***************************************************************************/
 
-static int ov76xx_configure(struct usb_ov511 *ov511)
+/* This initializes the OV7610, OV7620, or OV7620AE sensor. The OV7620AE uses
+ * the same register settings as the OV7610, since they are very similar.
+ */
+static int 
+ov7xx0_configure(struct usb_ov511 *ov511)
 {
-	struct usb_device *dev = ov511->dev;
 	int i, success;
 	int rc;
 
@@ -2832,9 +5863,11 @@
 		{ OV511_I2C_BUS, 0x23, 0x2a },
 		{ OV511_I2C_BUS, 0x24, 0x10 },
 		{ OV511_I2C_BUS, 0x25, 0x8a },
+		{ OV511_I2C_BUS, 0x26, 0xa2 },
 		{ OV511_I2C_BUS, 0x27, 0xc2 },
 		{ OV511_I2C_BUS, 0x2a, 0x04 },
 		{ OV511_I2C_BUS, 0x2c, 0xfe },
+		{ OV511_I2C_BUS, 0x2d, 0x93 },
 		{ OV511_I2C_BUS, 0x30, 0x71 },
 		{ OV511_I2C_BUS, 0x31, 0x60 },
 		{ OV511_I2C_BUS, 0x32, 0x26 },
@@ -2848,81 +5881,96 @@
 	};
 
 	static struct ov511_regvals aRegvalsNorm7620[] = {
-		{ OV511_I2C_BUS, 0x10, 0xff },
-		{ OV511_I2C_BUS, 0x16, 0x06 },
-		{ OV511_I2C_BUS, 0x28, 0x24 },
-		{ OV511_I2C_BUS, 0x2b, 0xac },
-		{ OV511_I2C_BUS, 0x12, 0x00 },
-		{ OV511_I2C_BUS, 0x28, 0x24 },
-		{ OV511_I2C_BUS, 0x0f, 0x85 },	/* lg's setting */
-		{ OV511_I2C_BUS, 0x15, 0x01 },
-		{ OV511_I2C_BUS, 0x23, 0x00 },
-		{ OV511_I2C_BUS, 0x24, 0x10 },
-		{ OV511_I2C_BUS, 0x25, 0x8a },
-		{ OV511_I2C_BUS, 0x27, 0xe2 },
-		{ OV511_I2C_BUS, 0x2a, 0x00 },
-		{ OV511_I2C_BUS, 0x2c, 0xfe },
-		{ OV511_I2C_BUS, 0x30, 0x71 },
-		{ OV511_I2C_BUS, 0x31, 0x60 },
-		{ OV511_I2C_BUS, 0x32, 0x26 },
-		{ OV511_I2C_BUS, 0x33, 0x20 },
-		{ OV511_I2C_BUS, 0x34, 0x48 },
-		{ OV511_I2C_BUS, 0x12, 0x24 },
-		{ OV511_I2C_BUS, 0x11, 0x01 },
+		{ OV511_I2C_BUS, 0x00, 0x00 },
+		{ OV511_I2C_BUS, 0x01, 0x80 },
+		{ OV511_I2C_BUS, 0x02, 0x80 },
+		{ OV511_I2C_BUS, 0x03, 0xc0 },
+		{ OV511_I2C_BUS, 0x06, 0x60 },
+		{ OV511_I2C_BUS, 0x07, 0x00 },
+		{ OV511_I2C_BUS, 0x0c, 0x24 },
 		{ OV511_I2C_BUS, 0x0c, 0x24 },
 		{ OV511_I2C_BUS, 0x0d, 0x24 },
+		{ OV511_I2C_BUS, 0x11, 0x01 },
+		{ OV511_I2C_BUS, 0x12, 0x24 },
+		{ OV511_I2C_BUS, 0x13, 0x01 },
+		{ OV511_I2C_BUS, 0x14, 0x84 },
+		{ OV511_I2C_BUS, 0x15, 0x01 },
+		{ OV511_I2C_BUS, 0x16, 0x03 },
+		{ OV511_I2C_BUS, 0x17, 0x2f },
+		{ OV511_I2C_BUS, 0x18, 0xcf },
+		{ OV511_I2C_BUS, 0x19, 0x06 },
+		{ OV511_I2C_BUS, 0x1a, 0xf5 },
+		{ OV511_I2C_BUS, 0x1b, 0x00 },
+		{ OV511_I2C_BUS, 0x20, 0x18 },
+		{ OV511_I2C_BUS, 0x21, 0x80 },
+		{ OV511_I2C_BUS, 0x22, 0x80 },
+		{ OV511_I2C_BUS, 0x23, 0x00 },
+		{ OV511_I2C_BUS, 0x26, 0xa2 },
+		{ OV511_I2C_BUS, 0x27, 0xea },
+		{ OV511_I2C_BUS, 0x28, 0x20 },
+		{ OV511_I2C_BUS, 0x29, 0x00 },
+		{ OV511_I2C_BUS, 0x2a, 0x10 },
+		{ OV511_I2C_BUS, 0x2b, 0x00 },
+		{ OV511_I2C_BUS, 0x2c, 0x88 },
+		{ OV511_I2C_BUS, 0x2d, 0x91 },
+		{ OV511_I2C_BUS, 0x2e, 0x80 },
+		{ OV511_I2C_BUS, 0x2f, 0x44 },
+		{ OV511_I2C_BUS, 0x60, 0x27 },
+		{ OV511_I2C_BUS, 0x61, 0x02 },
+		{ OV511_I2C_BUS, 0x62, 0x5f },
+		{ OV511_I2C_BUS, 0x63, 0xd5 },
+		{ OV511_I2C_BUS, 0x64, 0x57 },
+		{ OV511_I2C_BUS, 0x65, 0x83 },
+		{ OV511_I2C_BUS, 0x66, 0x55 },
+		{ OV511_I2C_BUS, 0x67, 0x92 },
+		{ OV511_I2C_BUS, 0x68, 0xcf },
+		{ OV511_I2C_BUS, 0x69, 0x76 },
+		{ OV511_I2C_BUS, 0x6a, 0x22 },
+		{ OV511_I2C_BUS, 0x6b, 0x00 },
+		{ OV511_I2C_BUS, 0x6c, 0x02 },
+		{ OV511_I2C_BUS, 0x6d, 0x44 },
+		{ OV511_I2C_BUS, 0x6e, 0x80 },
+		{ OV511_I2C_BUS, 0x6f, 0x1d },
+		{ OV511_I2C_BUS, 0x70, 0x8b },
+		{ OV511_I2C_BUS, 0x71, 0x00 },
+		{ OV511_I2C_BUS, 0x72, 0x14 },
+		{ OV511_I2C_BUS, 0x73, 0x54 },
+		{ OV511_I2C_BUS, 0x74, 0x00 },
+		{ OV511_I2C_BUS, 0x75, 0x8e },
+		{ OV511_I2C_BUS, 0x76, 0x00 },
+		{ OV511_I2C_BUS, 0x77, 0xff },
+		{ OV511_I2C_BUS, 0x78, 0x80 },
+		{ OV511_I2C_BUS, 0x79, 0x80 },
+		{ OV511_I2C_BUS, 0x7a, 0x80 },
+		{ OV511_I2C_BUS, 0x7b, 0xe2 },
+		{ OV511_I2C_BUS, 0x7c, 0x00 },
 		{ OV511_DONE_BUS, 0x0, 0x00 },
 	};
 
-	PDEBUG (4, "starting configuration");
+	PDEBUG(4, "starting configuration");
 
 	/* This looks redundant, but is necessary for WebCam 3 */
-	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE,
-	                    OV7610_I2C_WRITE_ID) < 0)
-		return -1;
-
-	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ,
-	                    OV7610_I2C_READ_ID) < 0)
-		return -1;
-
-	if (ov511_reset(dev, OV511_RESET_NOREGS) < 0)
+	ov511->primary_i2c_slave = OV7xx0_I2C_WRITE_ID;
+	if (ov51x_set_slave_ids(ov511, OV7xx0_I2C_WRITE_ID,
+				OV7xx0_I2C_READ_ID) < 0)
 		return -1;
 
-	/* Reset the 76xx */ 
-	if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1;
-
-	/* Wait for it to initialize */ 
-	schedule_timeout (1 + 150 * HZ / 1000);
-
-	for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) {
-		if ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) == 0x7F) &&
-		    (ov511_i2c_read(dev, OV7610_REG_ID_LOW) == 0xA2)) {
-			success = 1;
-			continue;
-		}
-
-		/* Reset the 76xx */ 
-		if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1;
-		/* Wait for it to initialize */ 
-		schedule_timeout (1 + 150 * HZ / 1000);
-		/* Dummy read to sync I2C */
-		if (ov511_i2c_read(dev, 0x00) < 0) return -1;
-	}
-
-	if (success) {
-		PDEBUG(1, "I2C synced in %d attempt(s) (method 1)", i);
+	if (ov51x_init_ov_sensor(ov511) >= 0) {
+		PDEBUG(1, "OV7xx0 sensor initalized (method 1)");
 	} else {
 		/* Reset the 76xx */
-		if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1;
+		if (ov51x_i2c_write(ov511, 0x12, 0x80) < 0) return -1;
 
 		/* Wait for it to initialize */
-		schedule_timeout (1 + 150 * HZ / 1000);
+		schedule_timeout(1 + 150 * HZ / 1000);
 
 		i = 0;
 		success = 0;
 		while (i <= i2c_detect_tries) {
-			if ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) == 0x7F) &&
-			    (ov511_i2c_read(dev, OV7610_REG_ID_LOW) == 0xA2)) {
+			if ((ov51x_i2c_read(ov511,
+					    OV7610_REG_ID_HIGH) == 0x7F) &&
+			    (ov51x_i2c_read(ov511,
+					    OV7610_REG_ID_LOW) == 0xA2)) {
 				success = 1;
 				break;
 			} else {
@@ -2930,311 +5978,705 @@
 			}
 		}
 
-		if ((i == i2c_detect_tries) && (success == 0)) {
-			err("Failed to read sensor ID. You might not have an OV7610/20,");
-			err("or it may be not responding. Report this to");
-			err("mwm@i.am");
-			return -1;
+// Was (i == i2c_detect_tries) previously. This obviously used to always report
+// success. Whether anyone actually depended on that bug is unknown
+		if ((i >= i2c_detect_tries) && (success == 0)) {
+			err("Failed to read sensor ID. You might not have an");
+			err("OV7610/20, or it may be not responding. Report");
+			err("this to " EMAIL);
+			err("This is only a warning. You can attempt to use");
+			err("your camera anyway");
+// Only issue a warning for now  
+//			return -1;
 		} else {
-			PDEBUG(1, "I2C synced in %d attempt(s) (method 2)", i+1);
+			PDEBUG(1, "OV7xx0 initialized (method 2, %dx)", i+1);
 		}
 	}
 
-	/* Detect sensor if user didn't use override param */
-	if (sensor == 0) {
-		rc = ov511_i2c_read(dev, OV7610_REG_COM_I);
+	/* Detect sensor (sub)type */
+	rc = ov51x_i2c_read(ov511, OV7610_REG_COM_I);
 
-		if (rc < 0) {
-			err("Error detecting sensor type");
-			return -1;
-		} else if((rc & 3) == 3) {
-			info("Sensor is an OV7610");
-			ov511->sensor = SEN_OV7610;
-		} else if((rc & 3) == 1) {
+	if (rc < 0) {
+		err("Error detecting sensor type");
+		return -1;
+	} else if ((rc & 3) == 3) {
+		info("Sensor is an OV7610");
+		ov511->sensor = SEN_OV7610;
+	} else if ((rc & 3) == 1) {
+		/* I don't know what's different about the 76BE yet */
+		if (ov51x_i2c_read(ov511, 0x15) & 1)
 			info("Sensor is an OV7620AE");
-			ov511->sensor = SEN_OV7620AE;
-		} else if((rc & 3) == 0) {
-			info("Sensor is an OV7620");
+		else
+			info("Sensor is an OV76BE");
+
+		/* OV511+ will return all zero isoc data unless we
+		 * configure the sensor as a 7620. Someone needs to
+		 * find the exact reg. setting that causes this. */
+		if (ov511->bridge == BRG_OV511PLUS) {
+			info("Enabling 511+/7620AE workaround");
 			ov511->sensor = SEN_OV7620;
 		} else {
-			err("Unknown image sensor version: %d", rc & 3);
+			ov511->sensor = SEN_OV7620AE;
+		}
+	} else if ((rc & 3) == 0) {
+		info("Sensor is an OV7620");
+		ov511->sensor = SEN_OV7620;
+	} else {
+		err("Unknown image sensor version: %d", rc & 3);
+		return -1;
+	}
+
+	if (ov511->sensor == SEN_OV7620) {
+		PDEBUG(4, "Writing 7620 registers");
+		if (ov511_write_regvals(ov511, aRegvalsNorm7620))
+			return -1;
+	} else {
+		PDEBUG(4, "Writing 7610 registers");
+		if (ov511_write_regvals(ov511, aRegvalsNorm7610))
+			return -1;
+	}
+
+	/* Set sensor-specific vars */
+	ov511->maxwidth = 640;
+	ov511->maxheight = 480;
+	ov511->minwidth = 64;
+	ov511->minheight = 48;
+
+	// FIXME: These do not match the actual settings yet
+	ov511->brightness = 0x80 << 8;
+	ov511->contrast = 0x80 << 8;
+	ov511->colour = 0x80 << 8;
+	ov511->hue = 0x80 << 8;
+
+	return 0;
+}
+
+/* This initializes the OV6620, OV6630, OV6630AE, or OV6630AF sensor. */
+static int 
+ov6xx0_configure(struct usb_ov511 *ov511)
+{
+	int rc;
+
+	static struct ov511_regvals aRegvalsNorm6x20[] = {
+		{ OV511_I2C_BUS, 0x12, 0x80 }, /* reset */
+		{ OV511_I2C_BUS, 0x11, 0x01 },
+		{ OV511_I2C_BUS, 0x03, 0x60 },
+		{ OV511_I2C_BUS, 0x05, 0x7f }, /* For when autoadjust is off */
+		{ OV511_I2C_BUS, 0x07, 0xa8 },
+		/* The ratio of 0x0c and 0x0d  controls the white point */
+		{ OV511_I2C_BUS, 0x0c, 0x24 },
+		{ OV511_I2C_BUS, 0x0d, 0x24 },
+		{ OV511_I2C_BUS, 0x12, 0x24 }, /* Enable AGC */
+		{ OV511_I2C_BUS, 0x14, 0x04 },
+		/* 0x16: 0x06 helps frame stability with moving objects */
+		{ OV511_I2C_BUS, 0x16, 0x06 },
+//		{ OV511_I2C_BUS, 0x20, 0x30 }, /* Aperture correction enable */
+		{ OV511_I2C_BUS, 0x26, 0xb2 }, /* BLC enable */
+		/* 0x28: 0x05 Selects RGB format if RGB on */
+		{ OV511_I2C_BUS, 0x28, 0x05 },
+		{ OV511_I2C_BUS, 0x2a, 0x04 }, /* Disable framerate adjust */
+//		{ OV511_I2C_BUS, 0x2b, 0xac }, /* Framerate; Set 2a[7] first */
+		{ OV511_I2C_BUS, 0x2d, 0x99 },
+		{ OV511_I2C_BUS, 0x34, 0xd2 }, /* Max A/D range */
+		{ OV511_I2C_BUS, 0x38, 0x8b },
+		{ OV511_I2C_BUS, 0x39, 0x40 },
+		
+		{ OV511_I2C_BUS, 0x3c, 0x39 }, /* Enable AEC mode changing */
+		{ OV511_I2C_BUS, 0x3c, 0x3c }, /* Change AEC mode */
+		{ OV511_I2C_BUS, 0x3c, 0x24 }, /* Disable AEC mode changing */
+
+		{ OV511_I2C_BUS, 0x3d, 0x80 },
+		/* These next two registers (0x4a, 0x4b) are undocumented. They
+		 * control the color balance */
+		{ OV511_I2C_BUS, 0x4a, 0x80 },
+		{ OV511_I2C_BUS, 0x4b, 0x80 },
+		{ OV511_I2C_BUS, 0x4d, 0xd2 }, /* This reduces noise a bit */
+		{ OV511_I2C_BUS, 0x4e, 0xc1 },
+		{ OV511_I2C_BUS, 0x4f, 0x04 },
+// Do 50-53 have any effect?
+// Toggle 0x12[2] off and on here?
+		{ OV511_DONE_BUS, 0x0, 0x00 },
+	};
+
+	/* This chip is undocumented so many of these are guesses. OK=verified,
+	 * A=Added since 6620, U=unknown function (not a 6620 reg) */
+	static struct ov511_regvals aRegvalsNorm6x30[] = {
+	/*OK*/	{ OV511_I2C_BUS, 0x12, 0x80 }, /* reset */
+	/*00?*/	{ OV511_I2C_BUS, 0x11, 0x01 },
+	/*OK*/	{ OV511_I2C_BUS, 0x03, 0x60 },
+	/*0A?*/	{ OV511_I2C_BUS, 0x05, 0x7f }, /* For when autoadjust is off */
+		{ OV511_I2C_BUS, 0x07, 0xa8 },
+		/* The ratio of 0x0c and 0x0d  controls the white point */
+	/*OK*/	{ OV511_I2C_BUS, 0x0c, 0x24 },
+	/*OK*/	{ OV511_I2C_BUS, 0x0d, 0x24 },
+	/*A*/	{ OV511_I2C_BUS, 0x0e, 0x20 },
+
+//	/*24?*/	{ OV511_I2C_BUS, 0x12, 0x28 }, /* Enable AGC */
+//		{ OV511_I2C_BUS, 0x12, 0x24 }, /* Enable AGC */
+
+//	/*A*/	{ OV511_I2C_BUS, 0x13, 0x21 },
+//	/*A*/	{ OV511_I2C_BUS, 0x13, 0x25 }, /* Tristate Y and UV busses */
+
+//	/*04?*/	{ OV511_I2C_BUS, 0x14, 0x80 },
+		/* 0x16: 0x06 helps frame stability with moving objects */
+	/*03?*/	{ OV511_I2C_BUS, 0x16, 0x06 },
+//	/*OK*/	{ OV511_I2C_BUS, 0x20, 0x30 }, /* Aperture correction enable */
+		// 21 & 22? The suggested values look wrong. Go with default
+	/*A*/	{ OV511_I2C_BUS, 0x23, 0xc0 },
+	/*A*/	{ OV511_I2C_BUS, 0x25, 0x9a }, // Check this against default
+//	/*OK*/	{ OV511_I2C_BUS, 0x26, 0xb2 }, /* BLC enable */
+
+		/* 0x28: 0x05 Selects RGB format if RGB on */
+//	/*04?*/	{ OV511_I2C_BUS, 0x28, 0x05 },
+//	/*04?*/	{ OV511_I2C_BUS, 0x28, 0x45 }, // DEBUG: Tristate UV bus
+
+	/*OK*/	{ OV511_I2C_BUS, 0x2a, 0x04 }, /* Disable framerate adjust */
+//	/*OK*/	{ OV511_I2C_BUS, 0x2b, 0xac }, /* Framerate; Set 2a[7] first */
+//	/*U*/	{ OV511_I2C_BUS, 0x2c, 0xa0 },
+		{ OV511_I2C_BUS, 0x2d, 0x99 },
+//	/*A*/	{ OV511_I2C_BUS, 0x33, 0x26 }, // Reserved bits on 6620
+//	/*d2?*/	{ OV511_I2C_BUS, 0x34, 0x03 }, /* Max A/D range */
+//	/*U*/	{ OV511_I2C_BUS, 0x36, 0x8f }, // May not be necessary
+//	/*U*/	{ OV511_I2C_BUS, 0x37, 0x80 }, // May not be necessary
+//	/*8b?*/	{ OV511_I2C_BUS, 0x38, 0x83 },
+//	/*40?*/	{ OV511_I2C_BUS, 0x39, 0xc0 }, // 6630 adds bit 7
+//		{ OV511_I2C_BUS, 0x3c, 0x39 }, /* Enable AEC mode changing */
+//		{ OV511_I2C_BUS, 0x3c, 0x3c }, /* Change AEC mode */
+//		{ OV511_I2C_BUS, 0x3c, 0x24 }, /* Disable AEC mode changing */
+	/*OK*/	{ OV511_I2C_BUS, 0x3d, 0x80 },
+//	/*A*/	{ OV511_I2C_BUS, 0x3f, 0x0e },
+//	/*U*/	{ OV511_I2C_BUS, 0x40, 0x00 },
+//	/*U*/	{ OV511_I2C_BUS, 0x41, 0x00 },
+//	/*U*/	{ OV511_I2C_BUS, 0x42, 0x80 },
+//	/*U*/	{ OV511_I2C_BUS, 0x43, 0x3f },
+//	/*U*/	{ OV511_I2C_BUS, 0x44, 0x80 },
+//	/*U*/	{ OV511_I2C_BUS, 0x45, 0x20 },
+//	/*U*/	{ OV511_I2C_BUS, 0x46, 0x20 },
+//	/*U*/	{ OV511_I2C_BUS, 0x47, 0x80 },
+//	/*U*/	{ OV511_I2C_BUS, 0x48, 0x7f },
+//	/*U*/	{ OV511_I2C_BUS, 0x49, 0x00 },
+
+		/* These next two registers (0x4a, 0x4b) are undocumented. They
+		 * control the color balance */
+//	/*OK?*/	{ OV511_I2C_BUS, 0x4a, 0x80 }, // Check these
+//	/*OK?*/	{ OV511_I2C_BUS, 0x4b, 0x80 },
+//	/*U*/	{ OV511_I2C_BUS, 0x4c, 0xd0 }, 
+	/*d2?*/	{ OV511_I2C_BUS, 0x4d, 0x10 }, /* This reduces noise a bit */
+	/*c1?*/	{ OV511_I2C_BUS, 0x4e, 0x40 },
+	/*04?*/	{ OV511_I2C_BUS, 0x4f, 0x07 },
+//	/*U*/	{ OV511_I2C_BUS, 0x50, 0xff },
+	/*U*/	{ OV511_I2C_BUS, 0x54, 0x23 },
+//	/*U*/	{ OV511_I2C_BUS, 0x55, 0xff },
+//	/*U*/	{ OV511_I2C_BUS, 0x56, 0x12 },
+	/*U*/	{ OV511_I2C_BUS, 0x57, 0x81 },
+//	/*U*/	{ OV511_I2C_BUS, 0x58, 0x75 },
+	/*U*/	{ OV511_I2C_BUS, 0x59, 0x01 },
+	/*U*/	{ OV511_I2C_BUS, 0x5a, 0x2c },
+	/*U*/	{ OV511_I2C_BUS, 0x5b, 0x0f },
+//	/*U*/	{ OV511_I2C_BUS, 0x5c, 0x10 },
+		{ OV511_DONE_BUS, 0x0, 0x00 },
+	};
+
+	PDEBUG(4, "starting sensor configuration");
+	
+	if (ov51x_init_ov_sensor(ov511) < 0) {
+		err("Failed to read sensor ID. You might not have an OV6xx0,");
+		err("or it may be not responding. Report this to " EMAIL);
+		return -1;
+	} else {
+		PDEBUG(1, "OV6xx0 sensor detected");
+	}
+
+	/* Detect sensor (sub)type */
+	rc = ov51x_i2c_read(ov511, OV7610_REG_COM_I);
+
+	if (rc < 0) {
+		err("Error detecting sensor type");
+		return -1;
+	} else if ((rc & 3) == 0) {
+		info("Sensor is an OV6630");
+		ov511->sensor = SEN_OV6630;
+	} else if ((rc & 3) == 1) {
+		info("Sensor is an OV6620");
+		ov511->sensor = SEN_OV6620;
+	} else if ((rc & 3) == 2) {
+		info("Sensor is an OV6630AE");
+		ov511->sensor = SEN_OV6630;
+	} else if ((rc & 3) == 3) {
+		info("Sensor is an OV6630AF");
+		ov511->sensor = SEN_OV6630;
+	} 
+
+	/* Set sensor-specific vars */
+	if (ov511->sensor == SEN_OV6620) {
+		ov511->maxwidth = 352;
+		ov511->maxheight = 288;
+	} else {
+		/* 352x288 not working with OV518 yet */
+		ov511->maxwidth = 320;
+		ov511->maxheight = 240;
+	}
+	ov511->minwidth = 64;
+	ov511->minheight = 48;
+
+	// FIXME: These do not match the actual settings yet
+	ov511->brightness = 0x80 << 8;
+	ov511->contrast = 0x80 << 8;
+	ov511->colour = 0x80 << 8;
+	ov511->hue = 0x80 << 8;
+
+	if (ov511->sensor == SEN_OV6620) {
+		PDEBUG(4, "Writing 6x20 registers");
+		if (ov511_write_regvals(ov511, aRegvalsNorm6x20))
+			return -1;
+	} else {
+		PDEBUG(4, "Writing 6x30 registers");
+		if (ov511_write_regvals(ov511, aRegvalsNorm6x30))
+			return -1;
+	}
+	
+	return 0;
+}
+
+/* This initializes the KS0127 and KS0127B video decoders. */
+static int 
+ks0127_configure(struct usb_ov511 *ov511)
+{
+	int rc;
+
+// FIXME: I don't know how to sync or reset it yet
+#if 0
+	if (ov51x_init_ks_sensor(ov511) < 0) {
+		err("Failed to initialize the KS0127");
+		return -1;
+	} else {
+		PDEBUG(1, "KS012x(B) sensor detected");
+	}
+#endif
+
+	/* Detect decoder subtype */
+	rc = ov51x_i2c_read(ov511, 0x00);
+	if (rc < 0) {
+		err("Error detecting sensor type");
+		return -1;
+	} else if (rc & 0x08) {
+		rc = ov51x_i2c_read(ov511, 0x3d);
+		if (rc < 0) {
+			err("Error detecting sensor type");
 			return -1;
+		} else if ((rc & 0x0f) == 0) {
+			info("Sensor is a KS0127");
+			ov511->sensor = SEN_KS0127;
+		} else if ((rc & 0x0f) == 9) {
+			info("Sensor is a KS0127B Rev. A");
+			ov511->sensor = SEN_KS0127B;
 		}
-	} else {	/* sensor != 0; user overrode detection */
-		ov511->sensor = sensor;
-		info("Sensor set to type %d", ov511->sensor);
+	} else {
+		err("Error: Sensor is an unsupported KS0122");
+		return -1;
 	}
 
-	if (ov511->sensor == SEN_OV7620) {
-		PDEBUG(4, "Writing 7620 registers");
-		if (ov511_write_regvals(dev, aRegvalsNorm7620))
-			return -1;
+	/* Set sensor-specific vars */
+	ov511->maxwidth = 640;
+	ov511->maxheight = 480;
+	ov511->minwidth = 64;
+	ov511->minheight = 48;
+
+	// FIXME: These do not match the actual settings yet
+	ov511->brightness = 0x80 << 8;
+	ov511->contrast = 0x80 << 8;
+	ov511->colour = 0x80 << 8;
+	ov511->hue = 0x80 << 8;
+
+	/* This device is not supported yet. Bail out now... */
+	err("This sensor is not supported yet.");
+	return -1;
+
+	return 0;
+}
+
+/* This initializes the SAA7111A video decoder. */
+static int 
+saa7111a_configure(struct usb_ov511 *ov511)
+{
+	struct usb_device *dev = ov511->dev;
+	int rc;
+
+	/* Since there is no register reset command, all registers must be
+	 * written, otherwise gives erratic results */
+	static struct ov511_regvals aRegvalsNormSAA7111A[] = {
+		{ OV511_I2C_BUS, 0x06, 0xce },
+		{ OV511_I2C_BUS, 0x07, 0x00 },
+		{ OV511_I2C_BUS, 0x10, 0x44 }, /* YUV422, 240/286 lines */
+		{ OV511_I2C_BUS, 0x0e, 0x01 }, /* NTSC M or PAL BGHI */
+		{ OV511_I2C_BUS, 0x00, 0x00 },
+		{ OV511_I2C_BUS, 0x01, 0x00 },
+		{ OV511_I2C_BUS, 0x03, 0x23 },
+		{ OV511_I2C_BUS, 0x04, 0x00 },
+		{ OV511_I2C_BUS, 0x05, 0x00 },
+		{ OV511_I2C_BUS, 0x08, 0xc8 }, /* Auto field freq */
+		{ OV511_I2C_BUS, 0x09, 0x01 }, /* Chrom. trap off, APER=0.25 */
+		{ OV511_I2C_BUS, 0x0a, 0x80 }, /* BRIG=128 */
+		{ OV511_I2C_BUS, 0x0b, 0x40 }, /* CONT=1.0 */
+		{ OV511_I2C_BUS, 0x0c, 0x40 }, /* SATN=1.0 */
+		{ OV511_I2C_BUS, 0x0d, 0x00 }, /* HUE=0 */
+		{ OV511_I2C_BUS, 0x0f, 0x00 },
+		{ OV511_I2C_BUS, 0x11, 0x0c },
+		{ OV511_I2C_BUS, 0x12, 0x00 },
+		{ OV511_I2C_BUS, 0x13, 0x00 },
+		{ OV511_I2C_BUS, 0x14, 0x00 },
+		{ OV511_I2C_BUS, 0x15, 0x00 },
+		{ OV511_I2C_BUS, 0x16, 0x00 },
+		{ OV511_I2C_BUS, 0x17, 0x00 },
+		{ OV511_I2C_BUS, 0x02, 0xc0 },	/* Composite input 0 */
+		{ OV511_DONE_BUS, 0x0, 0x00 },
+	};
+
+// FIXME: I don't know how to sync or reset it yet
+#if 0
+	if (ov51x_init_saa_sensor(ov511) < 0) {
+		err("Failed to initialize the SAA7111A");
+		return -1;
 	} else {
-		PDEBUG(4, "Writing 7610 registers");
-		if (ov511_write_regvals(dev, aRegvalsNorm7610))
-			return -1;
+		PDEBUG(1, "SAA7111A sensor detected");
 	}
+#endif
 
 	/* Set sensor-specific vars */
 	ov511->maxwidth = 640;
-	ov511->maxheight = 480;
+	ov511->maxheight = 480;		/* Even/Odd fields */
+	ov511->minwidth = 320;
+	ov511->minheight = 240;		/* Even field only */
+
+	ov511->has_decoder = 1;
+	ov511->num_inputs = 8;
+	ov511->norm = VIDEO_MODE_AUTO;
+	ov511->stop_during_set = 0;	/* Decoder guarantees stable image */
+
+	/* Decoder doesn't change these values, so we use these instead of
+	 * acutally reading the registers (which doesn't work) */
+	ov511->brightness = 0x80 << 8;
+	ov511->contrast = 0x40 << 9;
+	ov511->colour = 0x40 << 9;
+	ov511->hue = 32768;
 
-	if (aperture < 0) {          /* go with the default */
-		if (ov511_i2c_write(dev, 0x26, 0xa2) < 0) return -1;
-	} else if (aperture <= 0xf) {  /* user overrode default */
-		if (ov511_i2c_write(dev, 0x26, (aperture << 4) + 2) < 0)
-			return -1;
-	} else {
-		err("Invalid setting for aperture; legal value: 0 - 15");
+	PDEBUG(4, "Writing SAA7111A registers");
+	if (ov511_write_regvals(ov511, aRegvalsNormSAA7111A))
 		return -1;
-	}
 
-	if (autoadjust) {
-		if (ov511_i2c_write(dev, 0x13, 0x01) < 0) return -1;
-		if (ov511_i2c_write(dev, 0x2d, 
-		     ov511->sensor==SEN_OV7620?0x91:0x93) < 0) return -1;
+	/* Detect version of decoder. This must be done after writing the
+         * initial regs or the decoder will lock up. */
+	rc = ov51x_i2c_read(ov511, 0x00);
+
+	if (rc < 0) {
+		err("Error detecting sensor version");
+		return -1;
 	} else {
-		if (ov511_i2c_write(dev, 0x13, 0x00) < 0) return -1;
-		if (ov511_i2c_write(dev, 0x2d, 
-		     ov511->sensor==SEN_OV7620?0x81:0x83) < 0) return -1;
-		ov511_i2c_write(dev, 0x28, ov511_i2c_read(dev, 0x28) | 8);
+		info("Sensor is an SAA7111A (version 0x%x)", rc);
+		ov511->sensor = SEN_SAA7111A;
 	}
 
+	// FIXME: Fix this for OV518(+)
+	/* Latch to negative edge of clock. Otherwise, we get incorrect
+	 * colors and jitter in the digital signal. */
+	if (ov511->bridge == BRG_OV511 || ov511->bridge == BRG_OV511PLUS)
+		ov511_reg_write(dev, 0x11, 0x00);
+	else
+		warn("SAA7111A not yet supported with OV518/OV518+");
+
 	return 0;
 }
 
-static int ov6xx0_configure(struct usb_ov511 *ov511)
+/* This initializes the OV511/OV511+ and the sensor */
+static int 
+ov511_configure(struct usb_ov511 *ov511)
 {
 	struct usb_device *dev = ov511->dev;
-	int i, success, rc;
+	int i;
 
-	static struct ov511_regvals aRegvalsNorm6x20[] = {
-		{ OV511_I2C_BUS, 0x12, 0x80 },  /* reset */
-		{ OV511_I2C_BUS, 0x11, 0x01 },
-		{ OV511_I2C_BUS, 0x03, 0xd0 },
-		{ OV511_I2C_BUS, 0x05, 0x7f },
-		{ OV511_I2C_BUS, 0x07, 0xa8 },
-		{ OV511_I2C_BUS, 0x0c, 0x24 },
-		{ OV511_I2C_BUS, 0x0d, 0x24 },
-		{ OV511_I2C_BUS, 0x10, 0xff },  /* ? */
-		{ OV511_I2C_BUS, 0x14, 0x04 },
-		{ OV511_I2C_BUS, 0x16, 0x06 },  /* ? */
-		{ OV511_I2C_BUS, 0x19, 0x04 },
-		{ OV511_I2C_BUS, 0x1a, 0x93 },
-		{ OV511_I2C_BUS, 0x20, 0x28 },
-		{ OV511_I2C_BUS, 0x27, 0xa2 },
-		{ OV511_I2C_BUS, 0x28, 0x24 },
-		{ OV511_I2C_BUS, 0x2a, 0x04 },  /* 84? */
-		{ OV511_I2C_BUS, 0x2b, 0xac },  /* a8? */
-		{ OV511_I2C_BUS, 0x2d, 0x95 },
-		{ OV511_I2C_BUS, 0x33, 0x28 },
-		{ OV511_I2C_BUS, 0x34, 0xc7 },
-		{ OV511_I2C_BUS, 0x38, 0x8b },
-		{ OV511_I2C_BUS, 0x3c, 0x5c },
-		{ OV511_I2C_BUS, 0x3d, 0x80 },
-		{ OV511_I2C_BUS, 0x3f, 0x00 },
-		{ OV511_I2C_BUS, 0x4a, 0x80 }, /* undocumented */
-		{ OV511_I2C_BUS, 0x4b, 0x80 }, /* undocumented */
-		{ OV511_I2C_BUS, 0x4d, 0xd2 },
-		{ OV511_I2C_BUS, 0x4e, 0xc1 },
-		{ OV511_I2C_BUS, 0x4f, 0x04 },
-		{ OV511_DONE_BUS, 0x0, 0x00 },
+	static struct ov511_regvals aRegvalsInit511[] = {
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7f },
+	 	{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 },
+	 	{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7f },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3f },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3d },
+		{ OV511_DONE_BUS, 0x0, 0x00},
 	};
 
-	PDEBUG (4, "starting sensor configuration");
-	
-	/* Reset the 6xx0 */ 
-	if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1;
+	static struct ov511_regvals aRegvalsNorm511[] = {
+		{ OV511_REG_BUS, OV511_REG_DRAM_ENABLE_FLOW_CONTROL, 0x01 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x01 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x03 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x01 },
+		{ OV511_REG_BUS, OV511_REG_FIFO_BITMASK, 0x1f },
+		{ OV511_REG_BUS, OV511_OMNICE_ENABLE, 0x00 },
+		{ OV511_REG_BUS, OV511_OMNICE_LUT_ENABLE, 0x03 },
+		{ OV511_DONE_BUS, 0x0, 0x00 },
+	};
 
-	/* Wait for it to initialize */ 
-	schedule_timeout (1 + 150 * HZ / 1000);
+	static struct ov511_regvals aRegvalsNorm511Plus[] = {
+		{ OV511_REG_BUS, OV511_REG_DRAM_ENABLE_FLOW_CONTROL, 0xff },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x01 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x03 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x01 },
+		{ OV511_REG_BUS, OV511_REG_FIFO_BITMASK, 0xff },
+		{ OV511_REG_BUS, OV511_OMNICE_ENABLE, 0x00 },
+		{ OV511_REG_BUS, OV511_OMNICE_LUT_ENABLE, 0x03 },
+		{ OV511_DONE_BUS, 0x0, 0x00 },
+	};
 
-	for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) {
-		if ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) == 0x7F) &&
-		    (ov511_i2c_read(dev, OV7610_REG_ID_LOW) == 0xA2)) {
-			success = 1;
-			continue;
-		}
+	PDEBUG(4, "");
 
-		/* Reset the 6xx0 */ 
-		if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1;
-		/* Wait for it to initialize */ 
-		schedule_timeout (1 + 150 * HZ / 1000);
-		/* Dummy read to sync I2C */
-		if (ov511_i2c_read(dev, 0x00) < 0) return -1;
+	ov511->customid = ov511_reg_read(dev, OV511_REG_SYSTEM_CUSTOM_ID);
+	if (ov511->customid < 0) {
+		err("Unable to read camera bridge registers");
+		goto error;
 	}
 
-	if (success) {
-		PDEBUG(1, "I2C synced in %d attempt(s)", i);
-	} else {
-		err("Failed to read sensor ID. You might not have an OV6xx0,");
-		err("or it may be not responding. Report this to");
-		err("mwm@i.am");
-		return -1;
+	ov511->desc = -1;
+	PDEBUG (1, "CustomID = %d", ov511->customid);
+	for (i = 0; clist[i].id >= 0; i++) {
+		if (ov511->customid == clist[i].id) {
+			info("model: %s", clist[i].description);
+			ov511->desc = i;
+			break;
+		}
 	}
 
-	/* Detect sensor if user didn't use override param */
-	if (sensor == 0) {
-		rc = ov511_i2c_read(dev, OV7610_REG_COM_I);
+	if (clist[i].id == -1) {
+		err("Camera type (%d) not recognized", ov511->customid);
+		err("Please notify " EMAIL " of the name,");
+		err("manufacturer, model, and this number of your camera.");
+		err("Also include the output of the detection process.");
+	} 
 
-		if (rc < 0) {
-			err("Error detecting sensor type");
-			return -1;
-		} else {
-			info("Sensor is an OV6xx0 (version %d)", rc & 3);
-			ov511->sensor = SEN_OV6620;
-		}
-	} else {	/* sensor != 0; user overrode detection */
-		ov511->sensor = sensor;
-		info("Sensor set to type %d", ov511->sensor);
+	if (clist[i].id == 6) {	/* USB Life TV (NTSC) */
+		ov511->tuner_type = 8;		/* Temic 4036FY5 3X 1981 */
 	}
 
-	/* Set sensor-specific vars */
-	ov511->maxwidth = 352;
-	ov511->maxheight = 288;
+	if (ov511_write_regvals(ov511, aRegvalsInit511)) goto error;
 
-	PDEBUG(4, "Writing 6x20 registers");
-	if (ov511_write_regvals(dev, aRegvalsNorm6x20))
-		return -1;
+	if (ov511->led_policy == LED_OFF || ov511->led_policy == LED_AUTO)
+		ov51x_led_control(ov511, 0);
 
-	if (aperture < 0) {          /* go with the default */
-		if (ov511_i2c_write(dev, 0x26, 0xa2) < 0) return -1;
-	} else if (aperture <= 0xf) {  /* user overrode default */
-		if (ov511_i2c_write(dev, 0x26, (aperture << 4) + 2) < 0)
-			return -1;
+	/* The OV511+ has undocumented bits in the flow control register.
+	 * Setting it to 0xff fixes the corruption with moving objects. */
+	if (ov511->bridge == BRG_OV511) {
+		if (ov511_write_regvals(ov511, aRegvalsNorm511)) goto error;
+	} else if (ov511->bridge == BRG_OV511PLUS) {
+		if (ov511_write_regvals(ov511, aRegvalsNorm511Plus)) goto error;
 	} else {
-		err("Invalid setting for aperture; legal value: 0 - 15");
-		return -1;
+		err("Invalid bridge");
 	}
 
-	if (autoadjust) {
-		if (ov511_i2c_write(dev, 0x13, 0x01) < 0) return -1;
-		if (ov511_i2c_write(dev, 0x2d, 
-		     ov511->sensor==SEN_OV7620?0x91:0x93) < 0) return -1;
+	if (ov511_init_compression(ov511)) goto error;
+
+	ov511_set_packet_size(ov511, 0);
+
+	ov511->snap_enabled = snapshot;	
+
+	/* Test for 7xx0 */
+	PDEBUG(3, "Testing for 0V7xx0");
+	ov511->primary_i2c_slave = OV7xx0_I2C_WRITE_ID;
+	if (ov51x_set_slave_ids(ov511, OV7xx0_I2C_WRITE_ID,
+				OV7xx0_I2C_READ_ID) < 0)
+		goto error;
+
+	if (ov51x_i2c_write(ov511, 0x12, 0x80) < 0) {
+		/* Test for 6xx0 */
+		PDEBUG(3, "Testing for 0V6xx0");
+		ov511->primary_i2c_slave = OV6xx0_I2C_WRITE_ID;
+		if (ov51x_set_slave_ids(ov511, OV6xx0_I2C_WRITE_ID,
+					OV6xx0_I2C_READ_ID) < 0)
+			goto error;
+
+		if (ov51x_i2c_write(ov511, 0x12, 0x80) < 0) {
+			/* Test for 8xx0 */
+			PDEBUG(3, "Testing for 0V8xx0");
+			ov511->primary_i2c_slave = OV8xx0_I2C_WRITE_ID;
+			if (ov51x_set_slave_ids(ov511, OV8xx0_I2C_WRITE_ID,
+						OV8xx0_I2C_READ_ID))
+				goto error;
+
+			if (ov51x_i2c_write(ov511, 0x12, 0x80) < 0) {
+				/* Test for SAA7111A */
+				PDEBUG(3, "Testing for SAA7111A");
+				ov511->primary_i2c_slave = SAA7111A_I2C_WRITE_ID;
+				if (ov51x_set_slave_ids(ov511, SAA7111A_I2C_WRITE_ID,
+							SAA7111A_I2C_READ_ID))
+					goto error;
+
+				if (ov51x_i2c_write(ov511, 0x0d, 0x00) < 0) {
+					/* Test for KS0127 */
+					PDEBUG(3, "Testing for KS0127");
+					ov511->primary_i2c_slave = KS0127_I2C_WRITE_ID;
+					if (ov51x_set_slave_ids(ov511, KS0127_I2C_WRITE_ID,
+								KS0127_I2C_READ_ID))
+						goto error;
+
+					if (ov51x_i2c_write(ov511, 0x10, 0x00) < 0) {
+						err("Can't determine sensor slave IDs");
+		 				goto error;
+					} else {
+						if(ks0127_configure(ov511) < 0) {
+							err("Failed to configure KS0127");
+	 						goto error;
+						}
+					}
+				} else {
+					if(saa7111a_configure(ov511) < 0) {
+						err("Failed to configure SAA7111A");
+	 					goto error;
+					}
+				}
+			} else {
+				err("Detected unsupported OV8xx0 sensor");
+				goto error;
+			}
+		} else {
+			if(ov6xx0_configure(ov511) < 0) {
+				err("Failed to configure OV6xx0");
+ 				goto error;
+			}
+		}
 	} else {
-		if (ov511_i2c_write(dev, 0x13, 0x00) < 0) return -1;
-		if (ov511_i2c_write(dev, 0x2d, 
-		     ov511->sensor==SEN_OV7620?0x81:0x83) < 0) return -1;
-		ov511_i2c_write(dev, 0x28, ov511_i2c_read(dev, 0x28) | 8);
+		if(ov7xx0_configure(ov511) < 0) {
+			err("Failed to configure OV7xx0");
+	 		goto error;
+		}
 	}
 
 	return 0;
-}
 
+error:
+	err("OV511 Config failed");
+
+	return -EBUSY;
+}
 
-static int ov511_configure(struct usb_ov511 *ov511)
+/* This initializes the OV518/OV518+ and the sensor */
+static int 
+ov518_configure(struct usb_ov511 *ov511)
 {
 	struct usb_device *dev = ov511->dev;
-	int i;
 
-	static struct ov511_regvals aRegvalsInit[] = {
-		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7f },
-	 	{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 },
-	 	{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7f },
-		{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 },
-		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3f },
-		{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 },
-		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3d },
+	static struct ov511_regvals aRegvalsInit518[] = {
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x40 },
+	 	{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0xe1 },
+	 	{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3e },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0xe1 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x00 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0xe1 },
+		{ OV511_REG_BUS, 0x46, 0x00 }, 
+		{ OV511_REG_BUS, 0x5d, 0x03 },
 		{ OV511_DONE_BUS, 0x0, 0x00},
 	};
 
-	static struct ov511_regvals aRegvalsNorm511[] = {
-		{ OV511_REG_BUS, OV511_REG_DRAM_ENABLE_FLOW_CONTROL, 0x01 },
-		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x02 },
-		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x00 },
-		{ OV511_REG_BUS, OV511_REG_FIFO_BITMASK, 0x1f },
-		{ OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_Y, 0x08 },
-		{ OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_UV, 0x01 },
-		{ OV511_REG_BUS, OV511_OMNICE_PREDICTION_VERT_Y, 0x08 },
-		{ OV511_REG_BUS, OV511_OMNICE_PREDICTION_VERT_UV, 0x01 },
-		{ OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_HORIZ_Y, 0x01 },
-		{ OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_HORIZ_UV, 0x01 },
-		{ OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_VERT_Y, 0x01 },
-		{ OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_VERT_UV, 0x01 },
-		{ OV511_REG_BUS, OV511_OMNICE_ENABLE, 0x06 },
-		{ OV511_REG_BUS, OV511_OMNICE_LUT_ENABLE, 0x03 },
+	/* New values, based on Windows driver. Since what they do is not
+	 * known yet, this may be incorrect. */
+	static struct ov511_regvals aRegvalsNorm518[] = {
+		{ OV511_REG_BUS, 0x52, 0x02 }, /* Reset snapshot */
+		{ OV511_REG_BUS, 0x52, 0x01 }, /* Enable snapshot */
+		{ OV511_REG_BUS, 0x31, 0x0f },
+		{ OV511_REG_BUS, 0x5d, 0x03 },
+		{ OV511_REG_BUS, 0x24, 0x9f },
+		{ OV511_REG_BUS, 0x25, 0x90 },
+		{ OV511_REG_BUS, 0x20, 0x00 }, /* Was 0x08 */
+		{ OV511_REG_BUS, 0x51, 0x04 },
+		{ OV511_REG_BUS, 0x71, 0x19 },
 		{ OV511_DONE_BUS, 0x0, 0x00 },
 	};
 
-	memcpy(&ov511->vdev, &ov511_template, sizeof(ov511_template));
+	PDEBUG(4, "");
 
-	for (i = 0; i < OV511_NUMFRAMES; i++)
-		init_waitqueue_head(&ov511->frame[i].wq);
+	/* First 5 bits of custom ID reg are a revision ID on OV518 */
+	info("Device revision %d",
+	     0x1F & ov511_reg_read(dev, OV511_REG_SYSTEM_CUSTOM_ID));
 
-	init_waitqueue_head(&ov511->wq);
+	if (ov511_write_regvals(ov511, aRegvalsInit518)) goto error;
+
+	/* Set LED GPIO pin to output mode */
+	if (ov511_reg_write_mask(dev, 0x57,0x00, 0x02) < 0) goto error;
+
+	/* LED is off by default with OV518; have to explicitly turn it on */
+	if (ov511->led_policy == LED_OFF || ov511->led_policy == LED_AUTO)
+		ov51x_led_control(ov511, 0);
+	else
+		ov51x_led_control(ov511, 1);
+
+	/* Don't require compression if dumppix is enabled; otherwise it's
+	 * required. OV518 has no uncompressed mode, to save RAM. */
+	if (!dumppix && !ov511->compress) {
+		ov511->compress = 1;
+		warn("Compression required with OV518...enabling");
+	}
+
+	if (ov511_write_regvals(ov511, aRegvalsNorm518)) goto error;
 
-	if (ov511_write_regvals(dev, aRegvalsInit)) goto error;
-	if (ov511_write_regvals(dev, aRegvalsNorm511)) goto error;
+	if (ov511_reg_write(dev, 0x2f,0x80) < 0) goto error;
+
+	if (ov518_init_compression(ov511)) goto error;
 
 	ov511_set_packet_size(ov511, 0);
 
-	ov511->snap_enabled = snapshot;	
+	ov511->snap_enabled = snapshot;
 
 	/* Test for 76xx */
-	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE,
-	                   OV7610_I2C_WRITE_ID) < 0)
-		goto error;
-
-	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ,
-	                   OV7610_I2C_READ_ID) < 0)
+	ov511->primary_i2c_slave = OV7xx0_I2C_WRITE_ID;
+	if (ov51x_set_slave_ids(ov511, OV7xx0_I2C_WRITE_ID,
+				OV7xx0_I2C_READ_ID) < 0)
 		goto error;
 
-	if (ov511_reset(dev, OV511_RESET_NOREGS) < 0)
-		goto error;
+	/* The OV518 must be more aggressive about sensor detection since
+	 * I2C write will never fail if the sensor is not present. We have
+	 * to try to initialize the sensor to detect its presence */
 
-	if (ov511_i2c_write(dev, 0x12, 0x80) < 0) {
+	if (ov51x_init_ov_sensor(ov511) < 0) {
 		/* Test for 6xx0 */
-		if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE,
-		                    OV6xx0_I2C_WRITE_ID) < 0)
-			goto error;
-
-		if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ,
-		                    OV6xx0_I2C_READ_ID) < 0)
+		ov511->primary_i2c_slave = OV6xx0_I2C_WRITE_ID;
+		if (ov51x_set_slave_ids(ov511, OV6xx0_I2C_WRITE_ID,
+					OV6xx0_I2C_READ_ID) < 0)
 			goto error;
 
-		if (ov511_reset(dev, OV511_RESET_NOREGS) < 0)
-			goto error;
+		if (ov51x_init_ov_sensor(ov511) < 0) {
+			/* Test for 8xx0 */
+			ov511->primary_i2c_slave = OV8xx0_I2C_WRITE_ID;
+			if (ov51x_set_slave_ids(ov511, OV8xx0_I2C_WRITE_ID,
+						OV8xx0_I2C_READ_ID) < 0)
+				goto error;
 
-		if (ov511_i2c_write(dev, 0x12, 0x80) < 0) {
-			err("Can't determine sensor slave IDs");
-			goto error;
-		}
-		
-		if(ov6xx0_configure(ov511) < 0) {
-			err("failed to configure OV6xx0");
- 			goto error;
+			if (ov51x_init_ov_sensor(ov511) < 0) {
+				err("Can't determine sensor slave IDs");
+ 				goto error;
+			} else {
+				err("Detected unsupported OV8xx0 sensor");
+				goto error;
+			}
+		} else {
+			if (ov6xx0_configure(ov511) < 0) {
+				err("Failed to configure OV6xx0");
+ 				goto error;
+			}
 		}
 	} else {
-		if(ov76xx_configure(ov511) < 0) {
-			err("failed to configure OV76xx");
+		if (ov7xx0_configure(ov511) < 0) {
+			err("Failed to configure OV7xx0");
 	 		goto error;
 		}
 	}
-	
-	/* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used
-	 * (using read() instead). */
-	for (i = 0; i < OV511_NUMFRAMES; i++) {
-		ov511->frame[i].width = ov511->maxwidth;
-		ov511->frame[i].height = ov511->maxheight;
-		ov511->frame[i].depth = 24;
-		ov511->frame[i].bytes_read = 0;
-		ov511->frame[i].segment = 0;
-		ov511->frame[i].format = VIDEO_PALETTE_RGB24;
-		ov511->frame[i].segsize = GET_SEGSIZE(ov511->frame[i].format);
-	}
 
-	/* Initialize to max width/height, RGB24 */
-	if (ov511_mode_init_regs(ov511, ov511->maxwidth, ov511->maxheight,
-				 VIDEO_PALETTE_RGB24, 0) < 0)
-		goto error;
+	// The OV518 cannot go as low as the sensor can
+	ov511->minwidth = 160;
+	ov511->minheight = 120;
 
 	return 0;
-	
+
 error:
-	usb_driver_release_interface(&ov511_driver,
-		&dev->actconfig->interface[ov511->iface]);
+	err("OV518 Config failed");
 
-	return -EBUSY;	
+	return -EBUSY;
 }
 
 
@@ -3245,12 +6687,13 @@
  ***************************************************************************/
 
 static void *
-ov511_probe(struct usb_device *dev, unsigned int ifnum,
-	const struct usb_device_id *id)
+ov51x_probe(struct usb_device *dev, unsigned int ifnum,
+	    const struct usb_device_id *id)
 {
 	struct usb_interface_descriptor *interface;
 	struct usb_ov511 *ov511;
 	int i;
+	int registered = 0;
 
 	PDEBUG(1, "probing for device...");
 
@@ -3271,98 +6714,147 @@
 
 	if ((ov511 = kmalloc(sizeof(*ov511), GFP_KERNEL)) == NULL) {
 		err("couldn't kmalloc ov511 struct");
-		goto error;
+		goto error_unlock;
 	}
 
 	memset(ov511, 0, sizeof(*ov511));
 
 	ov511->dev = dev;
 	ov511->iface = interface->bInterfaceNumber;
+	ov511->led_policy = led;
+	ov511->compress = compress;
+	ov511->lightfreq = lightfreq;
+	ov511->num_inputs = 1;	   /* Video decoder init functs. change this */
+	ov511->stop_during_set = !fastset;
+	ov511->tuner_type = tuner;
+	ov511->backlight = backlight;
+
+	ov511->auto_brt = autobright;
+	ov511->auto_gain = autogain;
+	ov511->auto_exp = autoexp;
 
 	switch (dev->descriptor.idProduct) {
-	case 0x0511:
+	case PROD_OV511:
 		info("USB OV511 camera found");
 		ov511->bridge = BRG_OV511;
+		ov511->bclass = BCL_OV511;
 		break;
-	case 0xA511:
+	case PROD_OV511PLUS:
 		info("USB OV511+ camera found");
 		ov511->bridge = BRG_OV511PLUS;
+		ov511->bclass = BCL_OV511;
 		break;
-	case 0x0002:
-		if (dev->descriptor.idVendor != 0x0813)
+	case PROD_OV518:
+		info("USB OV518 camera found");
+		ov511->bridge = BRG_OV518;
+		ov511->bclass = BCL_OV518;
+		break;
+	case PROD_OV518PLUS:
+		info("USB OV518+ camera found");
+		ov511->bridge = BRG_OV518PLUS;
+		ov511->bclass = BCL_OV518;
+		break;
+	case PROD_ME2CAM:
+		if (dev->descriptor.idVendor != VEND_MATTEL)
 			goto error;
 		info("Intel Play Me2Cam (OV511+) found");
 		ov511->bridge = BRG_OV511PLUS;
+		ov511->bclass = BCL_OV511;
 		break;
 	default:
-		err("Unknown product ID");
-		goto error;
+		err("Unknown product ID 0x%x", dev->descriptor.idProduct);
+		goto error_dealloc;
 	}
 
-	ov511->customid = ov511_reg_read(dev, OV511_REG_SYSTEM_CUSTOM_ID);
-	if (ov511->customid < 0) {
-		err("Unable to read camera bridge registers");
-		goto error;
+	/* Workaround for some applications that want data in RGB
+	 * instead of BGR. */
+	if (force_rgb)
+		info("data format set to RGB");
+
+	init_waitqueue_head(&ov511->wq);
+
+	init_MUTEX(&ov511->lock);	/* to 1 == available */
+	init_MUTEX(&ov511->buf_lock);
+	init_MUTEX(&ov511->param_lock);
+	init_MUTEX(&ov511->i2c_lock);
+	ov511->buf_state = BUF_NOT_ALLOCATED;
+
+	if (ov511->bridge == BRG_OV518 ||
+	    ov511->bridge == BRG_OV518PLUS) {
+		if (ov518_configure(ov511) < 0)
+			goto error;
+	} else {
+		if (ov511_configure(ov511) < 0)
+			goto error;
 	}
 
-	ov511->desc = -1;
-	PDEBUG (4, "CustomID = %d", ov511->customid);
-	for (i = 0; clist[i].id >= 0; i++) {
-		if (ov511->customid == clist[i].id) {
-			info("camera: %s", clist[i].description);
-			ov511->desc = i;
-			break;
-		}
+	for (i = 0; i < OV511_NUMFRAMES; i++) {
+		ov511->frame[i].framenum = i;
+		init_waitqueue_head(&ov511->frame[i].wq);
 	}
 
-	/* Lifeview USB Life TV not supported */
-	if (clist[i].id == 38) {
-		err("This device is not supported yet.");
+	/* Unnecessary? (This is done on open(). Need to make sure variables
+	 * are properly initialized without this before removing it, though). */
+	if (ov51x_set_default_params(ov511) < 0)
 		goto error;
-	}
 
-	if (clist[i].id == -1) {
-		err("Camera type (%d) not recognized", ov511->customid);
-		err("Please contact mwm@i.am to request");
-		err("support for your camera.");
-	}
+#ifdef OV511_DEBUG
+	if (dump_bridge)
+		ov511_dump_regs(dev);
+#endif
 
-	/* Workaround for some applications that want data in RGB
-	 * instead of BGR */
-	if (force_rgb)
-		info("data format set to RGB");
+	memcpy(&ov511->vdev, &ov511_template, sizeof(ov511_template));
+	ov511->vdev.priv = ov511;
 
-	if (!ov511_configure(ov511)) {
-		ov511->user = 0;
-		init_MUTEX(&ov511->lock);	/* to 1 == available */
-		init_MUTEX(&ov511->buf_lock);
-		ov511->buf_state = BUF_NOT_ALLOCATED;
-	} else {
-		err("Failed to configure camera");
-		goto error;
+	for (i = 0; i < OV511_MAX_UNIT_VIDEO; i++) {
+		/* Minor 0 cannot be specified; assume user wants autodetect */
+		if (unit_video[i] == 0)
+			break;
+
+		if (video_register_device(&ov511->vdev, VFL_TYPE_GRABBER,
+			unit_video[i]) >= 0) {
+			registered = 1;
+			break;
+		}
 	}
 
-	if (video_register_device(&ov511->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
+	/* Use the next available one */
+	if (!registered &&
+	    video_register_device(&ov511->vdev, VFL_TYPE_GRABBER, -1) < 0) {
 		err("video_register_device failed");
 		goto error;
 	}
 
+	info("Device registered on minor %d", ov511->vdev.minor);
+
 	MOD_DEC_USE_COUNT;
      	return ov511;
 
 error:
+	err("Camera initialization failed");
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+	/* Safe to call even if entry doesn't exist */
+	destroy_proc_ov511_cam(ov511);
+#endif
+
+	usb_driver_release_interface(&ov511_driver,
+		&dev->actconfig->interface[ov511->iface]);
+
+error_dealloc:
 	if (ov511) {
 		kfree(ov511);
 		ov511 = NULL;
 	}
 
+error_unlock:
 	MOD_DEC_USE_COUNT;
 	return NULL;
 }
 
 
 static void
-ov511_disconnect(struct usb_device *dev, void *ptr)
+ov51x_disconnect(struct usb_device *dev, void *ptr)
 {
 	struct usb_ov511 *ov511 = (struct usb_ov511 *) ptr;
 	int n;
@@ -3422,8 +6914,8 @@
 static struct usb_driver ov511_driver = {
 	name:		"ov511",
 	id_table:       device_table,
-	probe:		ov511_probe,
-	disconnect:	ov511_disconnect
+	probe:		ov51x_probe,
+	disconnect:	ov51x_disconnect
 };
 
 
@@ -3433,7 +6925,88 @@
  *
  ***************************************************************************/
 
-static int __init usb_ov511_init(void)
+/* Returns 0 for success */
+int 
+ov511_register_decomp_module(int ver, struct ov51x_decomp_ops *ops, int ov518,
+			     int mmx)
+{
+	if (ver != DECOMP_INTERFACE_VER) {
+		err("Decompression module has incompatible");
+		err("interface version %d", ver);
+		err("Interface version %d is required", DECOMP_INTERFACE_VER);
+		return -EINVAL;
+	}
+
+	if (!ops)
+		return -EFAULT;
+
+	if (mmx && !ov51x_mmx_available) {
+		err("MMX not available on this system or kernel");
+		return -EINVAL;
+	}
+
+	lock_kernel();
+
+	if (ov518) {
+		if (mmx) {
+			if (ov518_mmx_decomp_ops)
+				goto err_in_use;
+			else
+				ov518_mmx_decomp_ops = ops;
+		} else {
+			if (ov518_decomp_ops)
+				goto err_in_use;
+			else
+				ov518_decomp_ops = ops;
+		}
+	} else {
+		if (mmx) {
+			if (ov511_mmx_decomp_ops)
+				goto err_in_use;
+			else
+				ov511_mmx_decomp_ops = ops;
+		} else {
+			if (ov511_decomp_ops)
+				goto err_in_use;
+			else
+				ov511_decomp_ops = ops;
+		}
+	}
+
+	MOD_INC_USE_COUNT;
+
+	unlock_kernel();
+	return 0;
+
+err_in_use:
+	unlock_kernel();
+	return -EBUSY;
+}
+
+void 
+ov511_deregister_decomp_module(int ov518, int mmx)
+{
+	lock_kernel();
+
+	if (ov518) {
+		if (mmx)
+			ov518_mmx_decomp_ops = NULL;
+		else
+			ov518_decomp_ops = NULL;
+	} else {
+		if (mmx)
+			ov511_mmx_decomp_ops = NULL;
+		else
+			ov511_decomp_ops = NULL;
+	}
+	
+	MOD_DEC_USE_COUNT;
+
+	unlock_kernel();
+}
+
+static int __init 
+usb_ov511_init(void)
 {
 #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
         proc_ov511_create();
@@ -3442,20 +7015,33 @@
 	if (usb_register(&ov511_driver) < 0)
 		return -1;
 
-	info(DRIVER_VERSION ":" DRIVER_DESC);
+	// FIXME: Don't know how to determine this yet
+	ov51x_mmx_available = 0;
+
+#if defined (__i386__)
+	if (test_bit(X86_FEATURE_MMX, &boot_cpu_data.x86_capability))
+		ov51x_mmx_available = 1;
+#endif
+
+	info(DRIVER_VERSION " : " DRIVER_DESC);
 
 	return 0;
 }
 
-static void __exit usb_ov511_exit(void)
+static void __exit 
+usb_ov511_exit(void)
 {
 	usb_deregister(&ov511_driver);
 	info("driver deregistered");
 
 #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
         proc_ov511_destroy();
-#endif 
+#endif
 }
 
 module_init(usb_ov511_init);
 module_exit(usb_ov511_exit);
+
+/* No version, for compatibility with binary-only modules */
+EXPORT_SYMBOL_NOVERS(ov511_register_decomp_module);
+EXPORT_SYMBOL_NOVERS(ov511_deregister_decomp_module);
diff -urN linux-2.4.18-pre2/drivers/usb/ov511.h linux/drivers/usb/ov511.h
--- linux-2.4.18-pre2/drivers/usb/ov511.h	Mon Dec 11 13:17:59 2000
+++ linux/drivers/usb/ov511.h	Thu Jan 10 13:28:35 2002
@@ -1,20 +1,41 @@
-
 #ifndef __LINUX_OV511_H
 #define __LINUX_OV511_H
 
 #include <asm/uaccess.h>
 #include <linux/videodev.h>
 #include <linux/smp_lock.h>
+#include <linux/usb.h>
 
 #define OV511_DEBUG	/* Turn on debug messages */
 
 #ifdef OV511_DEBUG
 #  define PDEBUG(level, fmt, args...) \
-if (debug >= level) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , ## args)
+if (debug >= (level)) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , \
+	## args)
 #else
 #  define PDEBUG(level, fmt, args...) do {} while(0)
 #endif
 
+/* This macro restricts an int variable to an inclusive range */
+#define RESTRICT_TO_RANGE(v,mi,ma) { \
+	if ((v) < (mi)) (v) = (mi); \
+	else if ((v) > (ma)) (v) = (ma); \
+}
+
+/* --------------------------------- */
+/* DEFINES FOR OV511 AND OTHER CHIPS */
+/* --------------------------------- */
+
+/* USB IDs */
+#define VEND_OMNIVISION	0x05A9
+#define PROD_OV511	0x0511
+#define PROD_OV511PLUS	0xA511
+#define PROD_OV518	0x0518
+#define PROD_OV518PLUS	0xA518
+
+#define VEND_MATTEL	0x0813
+#define PROD_ME2CAM	0x0002
+
 /* Camera interface register numbers */
 #define OV511_REG_CAMERA_DELAY_MODE		0x10
 #define OV511_REG_CAMERA_EDGE_MODE		0x11
@@ -52,6 +73,7 @@
 
 /* I2C register numbers */
 #define OV511_REG_I2C_CONTROL			0x40
+#define OV518_REG_I2C_CONTROL			0x47	/* OV518(+) only */
 #define OV511_REG_I2C_SLAVE_ID_WRITE		0x41
 #define OV511_REG_I2C_SUB_ADDRESS_3_BYTE	0x42
 #define OV511_REG_I2C_SUB_ADDRESS_2_BYTE	0x43
@@ -78,8 +100,16 @@
 #define OV511_REG_SYSTEM_CLOCK_DIVISOR		0x51
 #define OV511_REG_SYSTEM_SNAPSHOT		0x52
 #define OV511_REG_SYSTEM_INIT         		0x53
-#define OV511_REG_SYSTEM_PWR_CLK		0x54	/* OV511+ only */
+#define OV511_REG_SYSTEM_PWR_CLK		0x54 /* OV511+/OV518(+) only */
 #define OV511_REG_SYSTEM_LED_CTL		0x55	/* OV511+ only */
+#define OV518_REG_GPIO_IN			0x55	/* OV518(+) only */
+#define OV518_REG_GPIO_OUT			0x56	/* OV518(+) only */
+#define OV518_REG_GPIO_CTL			0x57	/* OV518(+) only */
+#define OV518_REG_GPIO_PULSE_IN			0x58	/* OV518(+) only */
+#define OV518_REG_GPIO_PULSE_CLEAR		0x59	/* OV518(+) only */
+#define OV518_REG_GPIO_PULSE_POLARITY		0x5a	/* OV518(+) only */
+#define OV518_REG_GPIO_PULSE_EN			0x5b	/* OV518(+) only */
+#define OV518_REG_GPIO_RESET			0x5c	/* OV518(+) only */
 #define OV511_REG_SYSTEM_USER_DEFINED		0x5E
 #define OV511_REG_SYSTEM_CUSTOM_ID		0x5F
 
@@ -119,6 +149,16 @@
 #define OV511PLUS_ALT_SIZE_769	6
 #define OV511PLUS_ALT_SIZE_961	7
 
+/* Alternate numbers for various max packet sizes (OV518(+) only) */
+#define OV518_ALT_SIZE_0	0
+#define OV518_ALT_SIZE_128	1
+#define OV518_ALT_SIZE_256	2
+#define OV518_ALT_SIZE_384	3
+#define OV518_ALT_SIZE_512	4
+#define OV518_ALT_SIZE_640	5
+#define OV518_ALT_SIZE_768	6
+#define OV518_ALT_SIZE_896	7
+
 /* OV7610 registers */
 #define OV7610_REG_GAIN          0x00	/* gain setting (5:0) */
 #define OV7610_REG_BLUE          0x01	/* blue channel balance */
@@ -170,44 +210,74 @@
 					/* 36-37 reserved */
 #define OV7610_REG_COM_K         0x38	/* misc registers */
 
-
-#define SCRATCH_BUF_SIZE 512
-
 #define FRAMES_PER_DESC		10	/* FIXME - What should this be? */
 #define FRAME_SIZE_PER_DESC	993	/* FIXME - Deprecated */
 #define MAX_FRAME_SIZE_PER_DESC	993	/* For statically allocated stuff */
+#define PIXELS_PER_SEG		256	/* Pixels per segment */
 
 #define OV511_ENDPOINT_ADDRESS 1	/* Isoc endpoint number */
 
-// CAMERA SPECIFIC
-// FIXME - these can vary between specific models
-#define OV7610_I2C_WRITE_ID 0x42
-#define OV7610_I2C_READ_ID  0x43
-#define OV6xx0_I2C_WRITE_ID 0xC0
-#define OV6xx0_I2C_READ_ID  0xC1
+/* I2C addresses */
+#define OV7xx0_I2C_WRITE_ID   0x42
+#define OV7xx0_I2C_READ_ID    0x43
+#define OV6xx0_I2C_WRITE_ID   0xC0
+#define OV6xx0_I2C_READ_ID    0xC1
+#define OV8xx0_I2C_WRITE_ID   0xA0
+#define OV8xx0_I2C_READ_ID    0xA1
+#define KS0127_I2C_WRITE_ID   0xD8
+#define KS0127_I2C_READ_ID    0xD9
+#define SAA7111A_I2C_WRITE_ID 0x48
+#define SAA7111A_I2C_READ_ID  0x49
 
 #define OV511_I2C_CLOCK_PRESCALER 0x03
 
-/* Prototypes */
-int usb_ov511_reg_read(struct usb_device *dev, unsigned char reg);
-int usb_ov511_reg_write(struct usb_device *dev,
-                        unsigned char reg,
-                        unsigned char value);
-
 /* Bridge types */
 enum {
+	BRG_UNKNOWN,
 	BRG_OV511,
 	BRG_OV511PLUS,
+	BRG_OV518,
+	BRG_OV518PLUS,
+};
+
+/* Bridge classes */
+enum {
+	BCL_UNKNOWN,
+	BCL_OV511,
+	BCL_OV518,
 };
 
 /* Sensor types */
 enum {
 	SEN_UNKNOWN,
+	SEN_OV76BE,
 	SEN_OV7610,
 	SEN_OV7620,
 	SEN_OV7620AE,
 	SEN_OV6620,
+	SEN_OV6630,
+	SEN_OV6630AE,
+	SEN_OV6630AF,
+	SEN_OV8600,
+	SEN_KS0127,
+	SEN_KS0127B,
+	SEN_SAA7111A,
+};
+
+// Not implemented yet
+#if 0
+/* Sensor classes */
+enum {
+	SCL_UNKNOWN,
+	SCL_OV7610,	/* 7610, 76BE, 7620AE (for now) */
+	SCL_OV7620,
+	SCL_OV6620,	
+	SCL_OV6630,	/* 6630, 6630AE, 6630AF */
+	SCL_OV8600,
+	SCL_KS0127,	/* SEN_KS0127, SEN_KS0127B */
+	SCL_SAA7111A,
 };
+#endif
 
 enum {
 	STATE_SCANNING,		/* Scanning for start */
@@ -222,7 +292,77 @@
 	BUF_PEND_DEALLOC,	/* ov511->buf_timer is set */
 };
 
-struct usb_device;
+/* --------- Definition of ioctl interface --------- */
+
+#define OV511_INTERFACE_VER 101
+
+/* LED options */
+enum {
+	LED_OFF,
+	LED_ON,
+	LED_AUTO,
+};
+
+/* Raw frame formats */
+enum {
+	RAWFMT_INVALID,
+	RAWFMT_YUV400,
+	RAWFMT_YUV420,
+	RAWFMT_YUV422,
+	RAWFMT_GBR422,
+};
+
+/* Unsigned short option numbers */
+enum {
+	OV511_USOPT_INVALID,
+	OV511_USOPT_BRIGHT,
+	OV511_USOPT_SAT,
+	OV511_USOPT_HUE,
+	OV511_USOPT_CONTRAST,
+};
+
+/* Unsigned int option numbers */
+enum {
+	OV511_UIOPT_INVALID,
+	OV511_UIOPT_POWER_FREQ,
+	OV511_UIOPT_BFILTER,
+	OV511_UIOPT_LED,
+	OV511_UIOPT_DEBUG,
+	OV511_UIOPT_COMPRESS,
+};
+
+struct ov511_ushort_opt {
+	int optnum;		/* Specific option number */
+	unsigned short val;
+};
+
+struct ov511_uint_opt {
+	int optnum;		/* Specific option number */
+	unsigned int val;
+};
+
+struct ov511_i2c_struct {
+	unsigned char slave; /* Write slave ID (read ID - 1) */
+	unsigned char reg;   /* Index of register */
+	unsigned char value; /* User sets this w/ write, driver does w/ read */
+	unsigned char mask;  /* Bits to be changed. Not used with read ops */
+};
+
+/* ioctls */
+#define OV511IOC_GINTVER  _IOR('v', BASE_VIDIOCPRIVATE + 0, int)
+#define OV511IOC_GUSHORT _IOWR('v', BASE_VIDIOCPRIVATE + 1, \
+			       struct ov511_ushort_opt)
+#define OV511IOC_SUSHORT  _IOW('v', BASE_VIDIOCPRIVATE + 2, \
+			       struct ov511_ushort_opt)
+#define OV511IOC_GUINT   _IOWR('v', BASE_VIDIOCPRIVATE + 3, \
+			       struct ov511_uint_opt)
+#define OV511IOC_SUINT    _IOW('v', BASE_VIDIOCPRIVATE + 4, \
+			       struct ov511_uint_opt)
+#define OV511IOC_WI2C     _IOW('v', BASE_VIDIOCPRIVATE + 5, \
+			       struct ov511_i2c_struct)
+#define OV511IOC_RI2C    _IOWR('v', BASE_VIDIOCPRIVATE + 6, \
+			       struct ov511_i2c_struct)
+/* ------------- End IOCTL interface -------------- */
 
 struct ov511_sbuf {
 	char *data;
@@ -248,36 +388,51 @@
 };
 
 struct ov511_frame {
+	int framenum;		/* Index of this frame */
 	char *data;		/* Frame buffer */
+	char *tempdata;		/* Temp buffer for multi-stage conversions */
+	char *rawdata;		/* Raw camera data buffer */
 
 	int depth;		/* Bytes per pixel */
 	int width;		/* Width application is expecting */
-	int height;		/* Height */
+	int height;		/* Height application is expecting */
 
-	int hdrwidth;		/* Width the frame actually is */
-	int hdrheight;		/* Height */
+	int rawwidth;		/* Actual width of frame sent from camera */
+	int rawheight;		/* Actual height of frame sent from camera */
 
 	int sub_flag;		/* Sub-capture mode for this frame? */
 	unsigned int format;	/* Format for this frame */
-	int segsize;		/* How big is each segment from the camera? */
+	int compressed;		/* Is frame compressed? */
 
 	volatile int grabstate;	/* State of grabbing */
 	int scanstate;		/* State of scanning */
 
-	int curline;		/* Line of frame we're working on */
-	int curpix;
-	int segment;		/* Segment from the incoming data */
+	int bytes_recvd;	/* Number of image bytes received from camera */
 
-	long scanlength;	/* uncompressed, raw data length of frame */
-	long bytes_read;	/* amount of scanlength that has been read from *data */
+	long bytes_read;	/* Amount that has been read() */
 
 	wait_queue_head_t wq;	/* Processes waiting */
 
 	int snapshot;		/* True if frame was a snapshot */
 };
 
+#define DECOMP_INTERFACE_VER 2
+
+/* Compression module operations */
+struct ov51x_decomp_ops {
+	int (*decomp_400)(unsigned char *, unsigned char *, int, int, int);
+	int (*decomp_420)(unsigned char *, unsigned char *, int, int, int);
+	int (*decomp_422)(unsigned char *, unsigned char *, int, int, int);
+	void (*decomp_lock)(void);
+	void (*decomp_unlock)(void);
+};
+
 #define OV511_NUMFRAMES	2
-#define OV511_NUMSBUF	2
+#if OV511_NUMFRAMES > VIDEO_MAX_FRAME
+#error "OV511_NUMFRAMES is too high"
+#endif
+
+#define OV511_NUMSBUF		2
 
 struct usb_ov511 {
 	struct video_device vdev;
@@ -292,22 +447,37 @@
 	/* Determined by sensor type */
 	int maxwidth;
 	int maxheight;
+	int minwidth;
+	int minheight;
 
 	int brightness;
 	int colour;
 	int contrast;
 	int hue;
 	int whiteness;
+	int exposure;
+	int auto_brt;		/* Auto brightness enabled flag */
+	int auto_gain;		/* Auto gain control enabled flag */
+	int auto_exp;		/* Auto exposure enabled flag */
+	int backlight;		/* Backlight exposure algorithm flag */
 
-	struct semaphore lock;
+	int led_policy;		/* LED: off|on|auto; OV511+ only */
+
+	struct semaphore lock;	/* Serializes user-accessible operations */
 	int user;		/* user count for exclusive use */
 
 	int streaming;		/* Are we streaming Isochronous? */
 	int grabbing;		/* Are we grabbing? */
 
 	int compress;		/* Should the next frame be compressed? */
+	int compress_inited;	/* Are compression params uploaded? */
+
+	int lightfreq;		/* Power (lighting) frequency */
+	int bandfilt;		/* Banding filter enabled flag */
 
 	char *fbuf;		/* Videodev buffer area */
+	char *tempfbuf;		/* Temporary (intermediate) buffer area */
+	char *rawfbuf;		/* Raw camera data buffer area */
 
 	int sub_flag;		/* Pix Array subcapture on flag */
 	int subx;		/* Pix Array subcapture x offset */
@@ -318,30 +488,53 @@
 	int curframe;		/* Current receiving sbuf */
 	struct ov511_frame frame[OV511_NUMFRAMES];	
 
-	int cursbuf;		/* Current receiving sbuf */
 	struct ov511_sbuf sbuf[OV511_NUMSBUF];
 
-	/* Scratch space from the Isochronous pipe */
-	unsigned char scratch[SCRATCH_BUF_SIZE];
-	int scratchlen;
-
 	wait_queue_head_t wq;	/* Processes waiting */
 
 	int snap_enabled;	/* Snapshot mode enabled */
 	
-	int bridge;		/* Type of bridge (OV511 or OV511+) */
-	int sensor;		/* Type of image sensor chip */
+	int bridge;		/* Type of bridge (BRG_*) */
+	int bclass;		/* Class of bridge (BCL_*) */
+	int sensor;		/* Type of image sensor chip (SEN_*) */
+	int sclass;		/* Type of image sensor chip (SCL_*) */
+	int tuner;		/* Type of TV tuner */
 
 	int packet_size;	/* Frame size per isoc desc */
 
-				/* proc interface */
 	struct semaphore param_lock;	/* params lock for this camera */
-	struct proc_dir_entry *proc_entry;	/* /proc/ov511/videoX */
-	
+
+	/* /proc entries, relative to /proc/video/ov511/ */
+	struct proc_dir_entry *proc_devdir;   /* Per-device proc directory */
+	struct proc_dir_entry *proc_info;     /* <minor#>/info entry */
+	struct proc_dir_entry *proc_button;   /* <minor#>/button entry */
+	struct proc_dir_entry *proc_control;  /* <minor#>/control entry */
+
 	/* Framebuffer/sbuf management */
 	int buf_state;
 	struct semaphore buf_lock;
 	struct timer_list buf_timer;
+
+	struct ov51x_decomp_ops *decomp_ops;
+
+	/* Stop streaming while changing picture settings */
+	int stop_during_set;
+
+	int stopped;		/* Streaming is temporarily paused */
+
+	/* Video decoder stuff */
+	int input;		/* Composite, S-VIDEO, etc... */
+	int num_inputs;		/* Number of inputs */
+	int norm; 		/* NTSC / PAL / SECAM */
+	int has_decoder;	/* Device has a video decoder */
+	int has_tuner;		/* Device has a TV tuner */
+	int has_audio_proc;	/* Device has an audio processor */
+	int freq;		/* Current tuner frequency */
+	int tuner_type;		/* Specific tuner model */
+
+	/* I2C interface to kernel */
+	struct semaphore i2c_lock;	  /* Protect I2C controller regs */
+	unsigned char primary_i2c_slave;  /* I2C write id of sensor */
 };
 
 struct cam_list {
@@ -354,18 +547,57 @@
 	char *name;
 };
 
-struct mode_list {
+struct mode_list_518 {
 	int width;
 	int height;
-	int color;		/* 0=grayscale, 1=color */
-	u8 pxcnt;		/* pixel counter */
-	u8 lncnt;		/* line counter */
-	u8 pxdv;		/* pixel divisor */
-	u8 lndv;		/* line divisor */
-	u8 m420;
-	u8 common_A;
-	u8 common_L;
-};
+	u8 reg28;
+	u8 reg29;
+	u8 reg2a;
+	u8 reg2c;
+	u8 reg2e;
+	u8 reg24;
+	u8 reg25;
+};
+
+/* Compression stuff */
+
+#define OV511_QUANTABLESIZE	64
+#define OV518_QUANTABLESIZE	32
+
+#define OV511_YQUANTABLE { \
+	0, 1, 1, 2, 2, 3, 3, 4, \
+	1, 1, 1, 2, 2, 3, 4, 4, \
+	1, 1, 2, 2, 3, 4, 4, 4, \
+	2, 2, 2, 3, 4, 4, 4, 4, \
+	2, 2, 3, 4, 4, 5, 5, 5, \
+	3, 3, 4, 4, 5, 5, 5, 5, \
+	3, 4, 4, 4, 5, 5, 5, 5, \
+	4, 4, 4, 4, 5, 5, 5, 5  \
+}
+
+#define OV511_UVQUANTABLE { \
+	0, 2, 2, 3, 4, 4, 4, 4, \
+	2, 2, 2, 4, 4, 4, 4, 4, \
+	2, 2, 3, 4, 4, 4, 4, 4, \
+	3, 4, 4, 4, 4, 4, 4, 4, \
+	4, 4, 4, 4, 4, 4, 4, 4, \
+	4, 4, 4, 4, 4, 4, 4, 4, \
+	4, 4, 4, 4, 4, 4, 4, 4, \
+	4, 4, 4, 4, 4, 4, 4, 4  \
+}
+
+#define OV518_YQUANTABLE { \
+	5, 4, 5, 6, 6, 7, 7, 7, \
+	5, 5, 5, 5, 6, 7, 7, 7, \
+	6, 6, 6, 6, 7, 7, 7, 8, \
+	7, 7, 6, 7, 7, 7, 8, 8  \
+}
+
+#define OV518_UVQUANTABLE { \
+	6, 6, 6, 7, 7, 7, 7, 7, \
+	6, 6, 6, 7, 7, 7, 7, 7, \
+	6, 6, 6, 7, 7, 7, 7, 8, \
+	7, 7, 7, 7, 7, 7, 8, 8  \
+}
 
 #endif
-
diff -urN linux-2.4.18-pre2/drivers/usb/pwc-ctrl.c linux/drivers/usb/pwc-ctrl.c
--- linux-2.4.18-pre2/drivers/usb/pwc-ctrl.c	Fri Dec 21 09:41:55 2001
+++ linux/drivers/usb/pwc-ctrl.c	Thu Jan 10 13:28:35 2002
@@ -1008,6 +1008,8 @@
 
 	if (pdev->type < 730)
 		return 0;
+	on_value /= 100;
+	off_value /= 100;
 	if (on_value < 0)
 		on_value = 0;
 	if (on_value > 0xff)
@@ -1048,8 +1050,8 @@
 
 	if (ret < 0)
 		return ret;
-	*on_value = buf[0];
-	*off_value = buf[1];
+	*on_value = buf[0] * 100;
+	*off_value = buf[1] * 100;
 	return 0;
 }
 
@@ -1175,6 +1177,8 @@
 			wb.read_red = pwc_read_red_gain(pdev);
 			wb.read_blue = pwc_read_blue_gain(pdev);
 		}
+		if (copy_to_user(arg, &wb, sizeof(wb)))
+			return -EFAULT;
 		break;
 	}
 
diff -urN linux-2.4.18-pre2/drivers/usb/pwc-if.c linux/drivers/usb/pwc-if.c
--- linux-2.4.18-pre2/drivers/usb/pwc-if.c	Fri Dec 21 09:41:55 2001
+++ linux/drivers/usb/pwc-if.c	Thu Jan 10 13:28:35 2002
@@ -39,7 +39,10 @@
 
 /* Contributors:
    - Alvarado: adding whitebalance code
-   - Alistar Moire: QuickCam 3000 Pro testing
+   - Alistar Moire: QuickCam 3000 Pro device/product ID
+   - Tony Hoyle: Creative Labs Webcam 5 device/product ID
+   - Mark Burazin: solving hang in VIDIOCSYNC when camera gets unplugged
+   - Jk Fang: SOTEC device/product ID
 */
 
 #include <linux/errno.h>
@@ -76,6 +79,8 @@
 	{ USB_DEVICE(0x046D, 0x08b0) },
 	{ USB_DEVICE(0x055D, 0x9000) },
 	{ USB_DEVICE(0x055D, 0x9001) },
+	{ USB_DEVICE(0x041E, 0x400C) },
+	{ USB_DEVICE(0x04CC, 0x8116) },
 	{ }
 };
 MODULE_DEVICE_TABLE(usb, pwc_device_table);
@@ -887,6 +892,7 @@
 	/* Stop camera, but only if we are sure the camera is still there */
 	if (!pdev->unplugged)
 		usb_set_interface(pdev->udev, 0, 0);
+	/* Unlinking ISOC buffers one by one */
 	for (i = MAX_ISO_BUFS - 1; i >= 0; i--) {
 		pdev->sbuf[i].urb->next = NULL;
 		usb_unlink_urb(pdev->sbuf[i].urb);
@@ -1493,6 +1499,12 @@
 			 */
 			add_wait_queue(&pdev->frameq, &wait);
 			while (pdev->full_frames == NULL) {
+				if (pdev->unplugged) {
+					remove_wait_queue(&pdev->frameq, &wait);
+					set_current_state(TASK_RUNNING);
+					return -ENODEV;
+				}
+			
 	                	if (signal_pending(current)) {
 	                		remove_wait_queue(&pdev->frameq, &wait);
 		                	set_current_state(TASK_RUNNING);
@@ -1710,7 +1722,29 @@
 			break;
 		}
 	}
-	else return NULL; /* Not Philips, Askey, Logitech or Samsung, for sure. */
+	else if (vendor_id == 0x041e) {
+		switch(product_id) {
+		case 0x400c:
+			Info("Creative Labs Webcam 5 detected.\n");
+			type_id = 730;
+			break;
+		default:
+			return NULL;
+			break;
+		}
+	}
+	else if (vendor_id == 0x04cc) { 
+		switch(product_id) {
+		case 0x8116:
+			Info("SOTEC CMS-001 USB webcam detected.\n");
+			type_id = 730;
+			break;  
+		default:
+			return NULL;
+			break;
+		}
+	}
+	else return NULL; /* Not Philips, Askey, Logitech, Samsung, Creative or SOTEC, for sure. */
 
 	memset(serial_number, 0, 30);
 	usb_string(udev, udev->descriptor.iSerialNumber, serial_number, 29);
@@ -1780,16 +1814,6 @@
 	if (hint < MAX_DEV_HINTS) 
 		device_hint[hint].pdev = pdev;
 
-#if 0
-	/* Shut down camera now (some people like the LED off) */
-	if (power_save) {
-		Trace(TRACE_PROBE, "Powering down camera");
-		i = pwc_camera_power(pdev, 0);
-		if (i < 0)
-			Info("Failed to power-down the camera (%d)\n", i);
-	}
-#endif
-
 	Trace(TRACE_PROBE, "probe() function returning struct at 0x%p.\n", pdev);
 	return pdev;
 }
@@ -1799,6 +1823,7 @@
 {
 	struct pwc_device *pdev;
 	int hint;
+	DECLARE_WAITQUEUE(wait, current);
 
 	lock_kernel();
 	free_mem_leak();
@@ -1833,13 +1858,19 @@
 			 */
 			wake_up(&pdev->frameq);
 			
-			/* Wait until we get a 'go' from _close(). This
-			   had a gigantic race condition, since we kfree()
+			/* Wait until we get a 'go' from _close(). This used
+			   to have a gigantic race condition, since we kfree()
 			   stuff here, but we have to wait until close() 
-			   is finished. */
+			   is finished. 
+			 */
 			   
 			Trace(TRACE_PROBE, "Sleeping on remove_ok.\n");
-			sleep_on(&pdev->remove_ok);
+			add_wait_queue(&pdev->remove_ok, &wait);
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			/* ... wait ... */
+			schedule();
+			remove_wait_queue(&pdev->remove_ok, &wait);
+			set_current_state(TASK_RUNNING);
 			Trace(TRACE_PROBE, "Done sleeping.\n");
 			set_mem_leak(pdev->vdev);
 			pdev->vdev = NULL;
@@ -1920,7 +1951,7 @@
 	char *sizenames[PSZ_MAX] = { "sqcif", "qsif", "qcif", "sif", "cif", "vga" };
 
 	Info("Philips PCA645/646 + PCVC675/680/690 + PCVC730/740/750 webcam module version " PWC_VERSION " loaded.\n");
-	Info("Also supports the Askey VC010, Logitech Quickcam 3000 Pro and the Samsung MPC-C10 and MPC-C30.\n");
+	Info("Also supports the Askey VC010, Logitech Quickcam 3000 Pro, Samsung MPC-C10 and MPC-C30, the Creative WebCam 5 and the SOTEC CMS-001.\n");
 
 	if (fps) {
 		if (fps < 5 || fps > 30) {
diff -urN linux-2.4.18-pre2/drivers/usb/pwc-ioctl.h linux/drivers/usb/pwc-ioctl.h
--- linux-2.4.18-pre2/drivers/usb/pwc-ioctl.h	Wed Oct 17 14:34:06 2001
+++ linux/drivers/usb/pwc-ioctl.h	Thu Jan 10 13:28:35 2002
@@ -79,8 +79,8 @@
 /* Used with VIDIOCPWC[SG]LED */
 struct pwc_leds
 {
-	int led_on;			/* Led on-time; range = 0..255 */
-	int led_off;			/*  */
+	int led_on;			/* Led on-time; range = 0..25000 */
+	int led_off;			/* Led off-time; range = 0..25000  */
 };
 
 
diff -urN linux-2.4.18-pre2/drivers/usb/pwc.h linux/drivers/usb/pwc.h
--- linux-2.4.18-pre2/drivers/usb/pwc.h	Fri Dec 21 09:41:55 2001
+++ linux/drivers/usb/pwc.h	Thu Jan 10 13:28:35 2002
@@ -60,8 +60,8 @@
 
 /* Version block */
 #define PWC_MAJOR	8
-#define PWC_MINOR	4
-#define PWC_VERSION 	"8.4"
+#define PWC_MINOR	5
+#define PWC_VERSION 	"8.5"
 #define PWC_NAME 	"pwc"
 
 /* Turn certain features on/off */
diff -urN linux-2.4.18-pre2/drivers/usb/storage/datafab.c linux/drivers/usb/storage/datafab.c
--- linux-2.4.18-pre2/drivers/usb/storage/datafab.c	Tue Oct  9 15:15:02 2001
+++ linux/drivers/usb/storage/datafab.c	Thu Jan 10 13:28:35 2002
@@ -208,7 +208,7 @@
 
 		if (use_sg) {
 			sg = (struct scatterlist *) dest;
-			buffer = kmalloc(len, GFP_KERNEL);
+			buffer = kmalloc(len, GFP_NOIO);
 			if (buffer == NULL)
 				return USB_STOR_TRANSPORT_ERROR;
 			ptr = buffer;
@@ -333,7 +333,7 @@
 
 		if (use_sg) {
 			sg = (struct scatterlist *) src;
-			buffer = kmalloc(len, GFP_KERNEL);
+			buffer = kmalloc(len, GFP_NOIO);
 			if (buffer == NULL)
 				return USB_STOR_TRANSPORT_ERROR;
 			ptr = buffer;
@@ -665,7 +665,7 @@
 	};
 
 	if (!us->extra) {
-		us->extra = kmalloc(sizeof(struct datafab_info), GFP_KERNEL);
+		us->extra = kmalloc(sizeof(struct datafab_info), GFP_NOIO);
 		if (!us->extra) {
 			US_DEBUGP("datafab_transport:  Gah! Can't allocate storage for Datafab info struct!\n");
 			return USB_STOR_TRANSPORT_ERROR;
diff -urN linux-2.4.18-pre2/drivers/usb/storage/freecom.c linux/drivers/usb/storage/freecom.c
--- linux-2.4.18-pre2/drivers/usb/storage/freecom.c	Tue Nov 13 09:19:41 2001
+++ linux/drivers/usb/storage/freecom.c	Thu Jan 10 13:28:35 2002
@@ -1,6 +1,6 @@
 /* Driver for Freecom USB/IDE adaptor
  *
- * $Id: freecom.c,v 1.19 2001/11/11 05:42:34 mdharm Exp $
+ * $Id: freecom.c,v 1.21 2001/12/29 03:47:33 mdharm Exp $
  *
  * Freecom v0.1:
  *
@@ -206,9 +206,7 @@
 
         return USB_STOR_TRANSPORT_GOOD;
 }
-#endif
 
-#if 0 /* Unused at this time */
 /* Read a value from an ide register. */
 static int
 freecom_ide_read (struct us_data *us, int reg, int *value)
@@ -435,7 +433,7 @@
 		/* Get the status again */
 		fcb->Type = FCM_PACKET_STATUS;
 		fcb->Timeout = 0;
-		memset (fcb->Atapi, 0, sizeof(fcb->Filler));
+		memset (fcb->Atapi, 0, sizeof(fcb->Atapi));
 		memset (fcb->Filler, 0, sizeof (fcb->Filler));
 
         	/* Send it out. */
@@ -487,10 +485,19 @@
          * and such will hang. */
         US_DEBUGP("Device indicates that it has %d bytes available\n",
                         le16_to_cpu (fst->Count));
+        US_DEBUGP("SCSI requested %d\n", usb_stor_transfer_length(srb));
 
         /* Find the length we desire to read. */
-        length = usb_stor_transfer_length (srb);
-        US_DEBUGP("SCSI requested %d\n", length);
+	switch (srb->cmnd[0]) {
+		case INQUIRY:
+		case REQUEST_SENSE:		/* 16 or 18 bytes? spec says 18, lots of devices only have 16 */
+		case MODE_SENSE:
+		case MODE_SENSE_10:
+			length = fst->Count;
+			break;
+		default:
+ 			length = usb_stor_transfer_length (srb);
+	}
 
 	/* verify that this amount is legal */
 	if (length > srb->request_bufflen) {
diff -urN linux-2.4.18-pre2/drivers/usb/storage/jumpshot.c linux/drivers/usb/storage/jumpshot.c
--- linux-2.4.18-pre2/drivers/usb/storage/jumpshot.c	Fri Sep 14 14:04:07 2001
+++ linux/drivers/usb/storage/jumpshot.c	Thu Jan 10 13:28:35 2002
@@ -284,7 +284,7 @@
 
                 if (use_sg) {
                         sg = (struct scatterlist *) dest;
-                        buffer = kmalloc(len, GFP_KERNEL);
+                        buffer = kmalloc(len, GFP_NOIO);
                         if (buffer == NULL)
                                 return USB_STOR_TRANSPORT_ERROR;
                         ptr = buffer;
@@ -399,7 +399,7 @@
 
                 if (use_sg) {
                         sg = (struct scatterlist *) src;
-                        buffer = kmalloc(len, GFP_KERNEL);
+                        buffer = kmalloc(len, GFP_NOIO);
                         if (buffer == NULL)
                                 return USB_STOR_TRANSPORT_ERROR;
                         ptr = buffer;
@@ -665,7 +665,7 @@
 
 
 	if (!us->extra) {
-		us->extra = kmalloc(sizeof(struct jumpshot_info), GFP_KERNEL);
+		us->extra = kmalloc(sizeof(struct jumpshot_info), GFP_NOIO);
 		if (!us->extra) {
 			US_DEBUGP("jumpshot_transport:  Gah! Can't allocate storage for jumpshot info struct!\n");
 			return USB_STOR_TRANSPORT_ERROR;
diff -urN linux-2.4.18-pre2/drivers/usb/storage/sddr09.c linux/drivers/usb/storage/sddr09.c
--- linux-2.4.18-pre2/drivers/usb/storage/sddr09.c	Fri Nov  9 14:37:14 2001
+++ linux/drivers/usb/storage/sddr09.c	Thu Jan 10 13:28:35 2002
@@ -1,6 +1,6 @@
 /* Driver for SanDisk SDDR-09 SmartMedia reader
  *
- * $Id: sddr09.c,v 1.21 2001/11/06 03:18:36 mdharm Exp $
+ * $Id: sddr09.c,v 1.22 2001/12/08 23:32:48 mdharm Exp $
  *
  * SDDR09 driver v0.1:
  *
@@ -79,7 +79,7 @@
 	// copy the data into the buffer.
 /*
 	if (xfer_len > 0) {
-		buffer = kmalloc(xfer_len, GFP_KERNEL);
+		buffer = kmalloc(xfer_len, GFP_NOIO);
 		if (!(command[0] & USB_DIR_IN))
 			memcpy(buffer, xfer_data, xfer_len);
 	}
@@ -303,7 +303,7 @@
 
 	if (use_sg) {
 		sg = (struct scatterlist *)content;
-		buffer = kmalloc(len, GFP_KERNEL);
+		buffer = kmalloc(len, GFP_NOIO);
 		if (buffer == NULL)
 			return USB_STOR_TRANSPORT_ERROR;
 		ptr = buffer;
@@ -630,17 +630,17 @@
 
 	alloc_blocks = (alloc_len + (1<<17) - 1) >> 17;
 	sg = kmalloc(alloc_blocks*sizeof(struct scatterlist),
-		GFP_KERNEL);
+		GFP_NOIO);
 	if (sg == NULL)
 		return 0;
 
 	for (i=0; i<alloc_blocks; i++) {
 		if (i<alloc_blocks-1) {
-			sg[i].address = kmalloc( (1<<17), GFP_KERNEL );
+			sg[i].address = kmalloc( (1<<17), GFP_NOIO );
 			sg[i].page = NULL;
 			sg[i].length = (1<<17);
 		} else {
-			sg[i].address = kmalloc(alloc_len, GFP_KERNEL);
+			sg[i].address = kmalloc(alloc_len, GFP_NOIO);
 			sg[i].page = NULL;
 			sg[i].length = alloc_len;
 		}
@@ -672,8 +672,8 @@
 		kfree(info->lba_to_pba);
 	if (info->pba_to_lba)
 		kfree(info->pba_to_lba);
-	info->lba_to_pba = kmalloc(numblocks*sizeof(int), GFP_KERNEL);
-	info->pba_to_lba = kmalloc(numblocks*sizeof(int), GFP_KERNEL);
+	info->lba_to_pba = kmalloc(numblocks*sizeof(int), GFP_NOIO);
+	info->pba_to_lba = kmalloc(numblocks*sizeof(int), GFP_NOIO);
 
 	if (info->lba_to_pba == NULL || info->pba_to_lba == NULL) {
 		if (info->lba_to_pba != NULL)
@@ -842,7 +842,7 @@
 
 	if (!us->extra) {
 		us->extra = kmalloc(
-			sizeof(struct sddr09_card_info), GFP_KERNEL);
+			sizeof(struct sddr09_card_info), GFP_NOIO);
 		if (!us->extra)
 			return USB_STOR_TRANSPORT_ERROR;
 		memset(us->extra, 0, sizeof(struct sddr09_card_info));
diff -urN linux-2.4.18-pre2/drivers/usb/storage/shuttle_usbat.c linux/drivers/usb/storage/shuttle_usbat.c
--- linux-2.4.18-pre2/drivers/usb/storage/shuttle_usbat.c	Sun Jul 29 21:11:50 2001
+++ linux/drivers/usb/storage/shuttle_usbat.c	Thu Jan 10 13:28:35 2002
@@ -1,6 +1,6 @@
 /* Driver for SCM Microsystems USB-ATAPI cable
  *
- * $Id: shuttle_usbat.c,v 1.14 2001/03/28 01:02:06 groovyjava Exp $
+ * $Id: shuttle_usbat.c,v 1.15 2001/12/08 23:32:48 mdharm Exp $
  *
  * Current development and maintenance by:
  *   (c) 2000, 2001 Robert Baruch (autophile@starband.net)
@@ -681,7 +681,7 @@
 
 	len = (65535/srb->transfersize) * srb->transfersize;
 	US_DEBUGP("Max read is %d bytes\n", len);
-	buffer = kmalloc(len, GFP_KERNEL);
+	buffer = kmalloc(len, GFP_NOIO);
 	if (buffer == NULL) // bloody hell!
 		return USB_STOR_TRANSPORT_FAILED;
 	sector = short_pack(data[7+3], data[7+2]);
diff -urN linux-2.4.18-pre2/drivers/usb/storage/transport.c linux/drivers/usb/storage/transport.c
--- linux-2.4.18-pre2/drivers/usb/storage/transport.c	Fri Nov  9 14:37:14 2001
+++ linux/drivers/usb/storage/transport.c	Thu Jan 10 13:28:35 2002
@@ -1,6 +1,6 @@
 /* Driver for USB Mass Storage compliant devices
  *
- * $Id: transport.c,v 1.41 2001/10/15 07:02:32 mdharm Exp $
+ * $Id: transport.c,v 1.42 2001/12/08 23:32:48 mdharm Exp $
  *
  * Current development and maintenance by:
  *   (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
@@ -388,7 +388,7 @@
 	devrequest *dr;
 
 	/* allocate the device request structure */
-	dr = kmalloc(sizeof(devrequest), GFP_KERNEL);
+	dr = kmalloc(sizeof(devrequest), GFP_NOIO);
 	if (!dr)
 		return -ENOMEM;
 
diff -urN linux-2.4.18-pre2/drivers/usb/storage/unusual_devs.h linux/drivers/usb/storage/unusual_devs.h
--- linux-2.4.18-pre2/drivers/usb/storage/unusual_devs.h	Thu Nov 22 11:49:34 2001
+++ linux/drivers/usb/storage/unusual_devs.h	Thu Jan 10 13:28:35 2002
@@ -1,7 +1,7 @@
 /* Driver for USB Mass Storage compliant devices
  * Ununsual Devices File
  *
- * $Id: unusual_devs.h,v 1.20 2001/09/02 05:12:57 mdharm Exp $
+ * $Id: unusual_devs.h,v 1.24 2001/12/29 03:12:45 mdharm Exp $
  *
  * Current development and maintenance by:
  *   (c) 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
@@ -58,6 +58,11 @@
 		"HP",
 		"CD-Writer+ 8200e",
 		US_SC_8070, US_PR_SCM_ATAPI, init_8200e, 0), 
+
+UNUSUAL_DEV(  0x03f0, 0x0307, 0x0001, 0x0001, 
+		"HP",
+		"CD-Writer+ CD-4e",
+		US_SC_8070, US_PR_SCM_ATAPI, init_8200e, 0), 
 #endif
 
 #ifdef CONFIG_USB_STORAGE_DPCM
@@ -86,6 +91,25 @@
 		"FinePix 1400Zoom",
 		US_SC_8070, US_PR_CBI, NULL, US_FL_FIX_INQUIRY),
 
+/* Reported by Peter Wächtler <pwaechtler@loewe-komp.de>
+ * The device needs the flags only.
+ */
+UNUSUAL_DEV(  0x04ce, 0x0002, 0x0074, 0x0074,
+		"ScanLogic",
+		"SL11R-IDE",
+		US_SC_SCSI, US_PR_BULK, NULL,
+		US_FL_FIX_INQUIRY),
+
+/* Reported by Kriston Fincher <kriston@airmail.net>
+ * Patch submitted by Sean Millichamp <sean@bruenor.org>
+ * This is to support the Panasonic PalmCam PV-SD4090
+ * This entry is needed because the device reports Sub=ff 
+ */
+UNUSUAL_DEV(  0x04da, 0x0901, 0x0100, 0x0200,
+               "Panasonic",
+               "LS-120 Camera",
+               US_SC_UFI, US_PR_CBI, NULL, 0),
+
 /* Most of the following entries were developed with the help of
  * Shuttle/SCM directly.
  */
@@ -161,14 +185,24 @@
 		US_SC_SCSI, US_PR_BULK, usb_stor_euscsi_init,
 		US_FL_SCM_MULT_TARG ),
 
+/* Iomega Clik! Drive 
+ * Reported by David Chatenay <dchatenay@hotmail.com>
+ * The reason this is needed is not fully known.
+ */
+UNUSUAL_DEV(  0x0525, 0xa140, 0x0100, 0x0100,
+		"Iomega",
+		"USB Clik! 40",
+		US_SC_8070, US_PR_BULK, NULL,
+		US_FL_FIX_INQUIRY | US_FL_START_STOP ),
+
 /* This entry is needed because the device reports Sub=ff */
-UNUSUAL_DEV(  0x054c, 0x0010, 0x0106, 0x0322, 
+UNUSUAL_DEV(  0x054c, 0x0010, 0x0106, 0x0422, 
 		"Sony",
 		"DSC-S30/S70/S75/505V/F505", 
 		US_SC_SCSI, US_PR_CB, NULL,
 		US_FL_SINGLE_LUN | US_FL_START_STOP | US_FL_MODE_XLATE ),
 
-/* Reported by win@geeks.nl */
+/* Reported by wim@geeks.nl */
 UNUSUAL_DEV(  0x054c, 0x0025, 0x0100, 0x0100, 
 		"Sony",
 		"Memorystick NW-MS7",
@@ -194,6 +228,13 @@
 		US_SC_UFI, US_PR_CB, NULL,
 		US_FL_SINGLE_LUN | US_FL_START_STOP ),
 		
+/* Submitted by Nathan Babb <nathan@lexi.com> */
+UNUSUAL_DEV(  0x054c, 0x006d, 0x0000, 0x9999,
+                "Sony",
+		"PEG Mass Storage",
+		US_SC_8070, US_PR_CBI, NULL,
+		US_FL_FIX_INQUIRY ),
+		
 UNUSUAL_DEV(  0x057b, 0x0000, 0x0000, 0x0299, 
 		"Y-E Data",
 		"Flashbuster-U",
@@ -264,6 +305,14 @@
 		US_FL_SINGLE_LUN | US_FL_START_STOP ),
 #endif
 
+/* Submitted by f.brugmans@hccnet.nl
+ * Needed for START_STOP flag */
+UNUSUAL_DEV( 0x0686, 0x4007, 0x0001, 0x0001,
+                "Minolta",
+                "Dimage S304",
+                US_SC_SCSI, US_PR_BULK, NULL,
+                US_FL_START_STOP ),
+
 UNUSUAL_DEV(  0x0693, 0x0002, 0x0100, 0x0100, 
 		"Hagiwara",
 		"FlashGate SmartMedia",
@@ -307,7 +356,7 @@
                 US_SC_QIC, US_PR_FREECOM, freecom_init, 0),
 #endif
 
-UNUSUAL_DEV(  0x07af, 0x0004, 0x0100, 0x0100, 
+UNUSUAL_DEV(  0x07af, 0x0004, 0x0100, 0x0133, 
 		"Microtech",
 		"USB-SCSI-DB25",
 		US_SC_SCSI, US_PR_BULK, usb_stor_euscsi_init,
@@ -374,16 +423,23 @@
 		"Simple Tech/Datafab CF+SM Reader",
 		US_SC_SCSI, US_PR_DATAFAB, NULL,
 		US_FL_MODE_XLATE | US_FL_START_STOP ),
+
+/* Submitted by Olaf Hering <olh@suse.de> */
+UNUSUAL_DEV(  0x07c4, 0xa109, 0x0000, 0xffff,
+		"Datafab Systems, Inc.",
+		"USB to CF + SM Combo (LC1)",
+		US_SC_SCSI, US_PR_DATAFAB, NULL,
+		US_FL_MODE_XLATE | US_FL_START_STOP ),
 #endif
 
-/* Casio QV 2x00/3x00/8000 digital still cameras are not conformant
+/* Casio QV 2x00/3x00/4000/8000 digital still cameras are not conformant
  * to the USB storage specification in two ways:
  * - They tell us they are using transport protocol CBI. In reality they
  *   are using transport protocol CB.
  * - They don't like the INQUIRY command. So we must handle this command
  *   of the SCSI layer ourselves.
  */
-UNUSUAL_DEV( 0x07cf, 0x1001, 0x9009, 0x9009,
+UNUSUAL_DEV( 0x07cf, 0x1001, 0x1000, 0x9009,
                 "Casio",
                 "QV DigitalCamera",
                 US_SC_8070, US_PR_CB, NULL,
@@ -402,3 +458,12 @@
                 US_SC_ISD200, US_PR_BULK, isd200_Initialization,
                 0 ),
 #endif
+
+/* Reported by Dan Pilone <pilone@slac.com>
+ * The device needs the flags only.
+ */
+UNUSUAL_DEV(  0x1065, 0x2136, 0x0000, 0x9999,
+		"CCYU TECHNOLOGY",
+		"EasyDisk Portable Device",
+		US_SC_SCSI, US_PR_BULK, NULL,
+		US_FL_MODE_XLATE | US_FL_START_STOP),
diff -urN linux-2.4.18-pre2/drivers/usb/usb-ohci.c linux/drivers/usb/usb-ohci.c
--- linux-2.4.18-pre2/drivers/usb/usb-ohci.c	Fri Dec 21 09:41:55 2001
+++ linux/drivers/usb/usb-ohci.c	Thu Jan 10 13:28:35 2002
@@ -76,7 +76,8 @@
 
 
 #ifdef CONFIG_PMAC_PBOOK
-#include <asm/feature.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
 #include <asm/pci-bridge.h>
 #ifndef CONFIG_PM
 #define CONFIG_PM
@@ -2699,12 +2700,12 @@
 	pci_write_config_word (dev, PCI_COMMAND, cmd);
 #ifdef CONFIG_PMAC_PBOOK
 	{
-   	struct device_node	*of_node;
+	   	struct device_node	*of_node;
 
-	/* Disable USB PAD & cell clock */
-	of_node = pci_device_to_OF_node (ohci->ohci_dev);
-	if (of_node && _machine == _MACH_Pmac)
-		feature_set_usb_power (of_node, 0);
+		/* Disable USB PAD & cell clock */
+		of_node = pci_device_to_OF_node (ohci->ohci_dev);
+		if (of_node)
+			pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0);
 	}
 #endif
 	return 0;
@@ -2729,12 +2730,12 @@
 
 #ifdef CONFIG_PMAC_PBOOK
 	{
-	struct device_node *of_node;
+		struct device_node *of_node;
 
-	/* Re-enable USB PAD & cell clock */
-	of_node = pci_device_to_OF_node (ohci->ohci_dev);
-	if (of_node && _machine == _MACH_Pmac)
-		feature_set_usb_power (of_node, 1);
+		/* Re-enable USB PAD & cell clock */
+		of_node = pci_device_to_OF_node (ohci->ohci_dev);
+		if (of_node)
+			pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 1);
 	}
 #endif
 
diff -urN linux-2.4.18-pre2/drivers/usb/usb-skeleton.c linux/drivers/usb/usb-skeleton.c
--- linux-2.4.18-pre2/drivers/usb/usb-skeleton.c	Mon Nov 12 09:53:56 2001
+++ linux/drivers/usb/usb-skeleton.c	Thu Jan 10 13:28:35 2002
@@ -138,9 +138,37 @@
 /* lock to protect the minor_table structure */
 static DECLARE_MUTEX (minor_table_mutex);
 
-/* file operations needed when we register this driver */
+/*
+ * File operations needed when we register this driver.
+ * This assumes that this driver NEEDS file operations,
+ * of course, which means that the driver is expected
+ * to have a node in the /dev directory. If the USB
+ * device were for a network interface then the driver
+ * would use "struct net_driver" instead, and a serial
+ * device would use "struct tty_driver". 
+ */
 static struct file_operations skel_fops = {
+	/*
+	 * The owner field is part of the module-locking
+	 * mechanism. The idea is that the kernel knows
+	 * which module to increment the use-counter of
+	 * BEFORE it calls the device's open() function.
+	 * This also means that the kernel can decrement
+	 * the use-counter again before calling release()
+	 * or should the open() function fail.
+	 *
+	 * Not all device structures have an "owner" field
+	 * yet. "struct file_operations" and "struct net_device"
+	 * do, while "struct tty_driver" does not. If the struct
+	 * has an "owner" field, then initialize it to the value
+	 * THIS_MODULE and the kernel will handle all module
+	 * locking for you automatically. Otherwise, you must
+	 * increment the use-counter in the open() function
+	 * and decrement it again in the release() function
+	 * yourself.
+	 */
 	owner:		THIS_MODULE,
+
 	read:		skel_read,
 	write:		skel_write,
 	ioctl:		skel_ioctl,
@@ -215,7 +243,11 @@
 		return -ENODEV;
 	}
 
-	/* increment our usage count for the module */
+	/* Increment our usage count for the module.
+	 * This is redundant here, because "struct file_operations"
+	 * has an "owner" field. This line is included here soley as
+	 * a reference for drivers using lesser structures... ;-)
+	 */
 	MOD_INC_USE_COUNT;
 
 	/* lock our minor table and get our local data for this minor */
@@ -278,8 +310,8 @@
 		/* the device was unplugged before the file was released */
 		up (&dev->sem);
 		skel_delete (dev);
-		MOD_DEC_USE_COUNT;
 		up (&minor_table_mutex);
+		MOD_DEC_USE_COUNT;
 		return 0;
 	}
 
diff -urN linux-2.4.18-pre2/drivers/usb/usbnet.c linux/drivers/usb/usbnet.c
--- linux-2.4.18-pre2/drivers/usb/usbnet.c	Thu Jan 10 13:28:30 2002
+++ linux/drivers/usb/usbnet.c	Thu Jan 10 13:28:35 2002
@@ -1571,7 +1571,7 @@
 	struct urb		*urb = 0;
 	struct skb_data		*entry;
 	struct driver_info	*info = dev->driver_info;
-	int			flags;
+	unsigned long		flags;
 #ifdef	CONFIG_USB_NET1080
 	struct nc_header	*header = 0;
 	struct nc_trailer	*trailer = 0;
diff -urN linux-2.4.18-pre2/fs/reiserfs/namei.c linux/fs/reiserfs/namei.c
--- linux-2.4.18-pre2/fs/reiserfs/namei.c	Thu Jan 10 13:28:30 2002
+++ linux/fs/reiserfs/namei.c	Thu Jan 10 13:28:35 2002
@@ -876,7 +876,7 @@
     }
 
     item_len = ROUND_UP (strlen (symname));
-    if (item_len > MAX_ITEM_LEN (dir->i_sb->s_blocksize)) {
+    if (item_len > MAX_DIRECT_ITEM_LEN (dir->i_sb->s_blocksize)) {
 	iput(inode) ;
 	return -ENAMETOOLONG;
     }
diff -urN linux-2.4.18-pre2/fs/ufs/super.c linux/fs/ufs/super.c
--- linux-2.4.18-pre2/fs/ufs/super.c	Thu Jan 10 13:28:30 2002
+++ linux/fs/ufs/super.c	Thu Jan 10 13:28:35 2002
@@ -981,3 +981,4 @@
 
 module_init(init_ufs_fs)
 module_exit(exit_ufs_fs)
+MODULE_LICENSE("GPL");
diff -urN linux-2.4.18-pre2/include/asm-cris/bitops.h linux/include/asm-cris/bitops.h
--- linux-2.4.18-pre2/include/asm-cris/bitops.h	Mon Oct  8 11:43:54 2001
+++ linux/include/asm-cris/bitops.h	Thu Jan 10 13:28:35 2002
@@ -331,6 +331,17 @@
 #define find_first_zero_bit(addr, size) \
         find_next_zero_bit((addr), (size), 0)
 
+/*
+ * hweightN - returns the hamming weight of a N-bit word
+ * @x: the word to weigh
+ *
+ * The Hamming Weight of a number is the total number of bits set in it.
+ */
+
+#define hweight32(x) generic_hweight32(x)
+#define hweight16(x) generic_hweight16(x)
+#define hweight8(x) generic_hweight8(x)
+
 #define ext2_set_bit                 test_and_set_bit
 #define ext2_clear_bit               test_and_clear_bit
 #define ext2_test_bit                test_bit
diff -urN linux-2.4.18-pre2/include/asm-cris/elf.h linux/include/asm-cris/elf.h
--- linux-2.4.18-pre2/include/asm-cris/elf.h	Tue May  1 16:05:00 2001
+++ linux/include/asm-cris/elf.h	Thu Jan 10 13:28:35 2002
@@ -9,8 +9,9 @@
 
 typedef unsigned long elf_greg_t;
 
-/* These probably need fixing.  */
-#define ELF_NGREG (sizeof (struct pt_regs) / sizeof(elf_greg_t))
+/* Note that NGREG is defined to ELF_NGREG in include/linux/elfcore.h, and is
+   thus exposed to user-space. */
+#define ELF_NGREG (sizeof (struct user_regs_struct) / sizeof(elf_greg_t))
 typedef elf_greg_t elf_gregset_t[ELF_NGREG];
 
 /* A placeholder; CRIS does not have any fp regs.  */
@@ -45,7 +46,52 @@
 	(_r)->r1 = 0;  (_r)->r0 = 0;  (_r)->mof = 0; (_r)->srp = 0; \
 } while (0)
 
-#undef USE_ELF_CORE_DUMP
+#define USE_ELF_CORE_DUMP
+
+/* The additional layer below is because the stack pointer is missing in 
+   the pt_regs struct, but needed in a core dump. pr_reg is a elf_gregset_t,
+   and should be filled in according to the layout of the user_regs_struct
+   struct; regs is a pt_regs struct. We dump all registers, though several are
+   obviously unnecessary. That way there's less need for intelligence at 
+   the receiving end (i.e. gdb). */
+#define ELF_CORE_COPY_REGS(pr_reg, regs)                   \
+	pr_reg[0] = regs->r0;                              \
+	pr_reg[1] = regs->r1;                              \
+	pr_reg[2] = regs->r2;                              \
+	pr_reg[3] = regs->r3;                              \
+	pr_reg[4] = regs->r4;                              \
+	pr_reg[5] = regs->r5;                              \
+	pr_reg[6] = regs->r6;                              \
+	pr_reg[7] = regs->r7;                              \
+	pr_reg[8] = regs->r8;                              \
+	pr_reg[9] = regs->r9;                              \
+	pr_reg[10] = regs->r10;                            \
+	pr_reg[11] = regs->r11;                            \
+	pr_reg[12] = regs->r12;                            \
+	pr_reg[13] = regs->r13;                            \
+	pr_reg[14] = rdusp();               /* sp */       \
+	pr_reg[15] = regs->irp;             /* pc */       \
+	pr_reg[16] = 0;                     /* p0 */       \
+	pr_reg[17] = rdvr();                /* vr */       \
+	pr_reg[18] = 0;                     /* p2 */       \
+	pr_reg[19] = 0;                     /* p3 */       \
+	pr_reg[20] = 0;                     /* p4 */       \
+	pr_reg[21] = (regs->dccr & 0xffff); /* ccr */      \
+	pr_reg[22] = 0;                     /* p6 */       \
+	pr_reg[23] = regs->mof;             /* mof */      \
+	pr_reg[24] = 0;                     /* p8 */       \
+	pr_reg[25] = 0;                     /* ibr */      \
+	pr_reg[26] = 0;                     /* irp */      \
+	pr_reg[27] = regs->srp;             /* srp */      \
+	pr_reg[28] = 0;                     /* bar */      \
+	pr_reg[29] = regs->dccr;            /* dccr */     \
+	pr_reg[30] = 0;                     /* brp */      \
+	pr_reg[31] = rdusp();               /* usp */      \
+	pr_reg[32] = 0;                     /* csrinstr */ \
+	pr_reg[33] = 0;                     /* csraddr */  \
+	pr_reg[34] = 0;                     /* csrdata */
+
+
 #define ELF_EXEC_PAGESIZE	8192
 
 /* This is the location that an ET_DYN program is loaded if exec'ed.  Typical
diff -urN linux-2.4.18-pre2/include/asm-cris/irq.h linux/include/asm-cris/irq.h
--- linux-2.4.18-pre2/include/asm-cris/irq.h	Mon Oct  8 11:43:54 2001
+++ linux/include/asm-cris/irq.h	Thu Jan 10 13:28:35 2002
@@ -126,6 +126,9 @@
   /* the asm IRQ handler makes sure the causing IRQ is blocked, then it calls
    * do_IRQ (with irq disabled still). after that it unblocks and jumps to
    * ret_from_intr (entry.S)
+   *
+   * The reason the IRQ is blocked is to allow an sti() before the handler which
+   * will acknowledge the interrupt is run.
    */
 
 #define BUILD_IRQ(nr,mask) \
@@ -151,6 +154,41 @@
           "reti\n\t" \
           "nop\n");
 
+/* This is subtle. The timer interrupt is crucial and it should not be disabled for 
+ * too long. However, if it had been a normal interrupt as per BUILD_IRQ, it would
+ * have been BLOCK'ed, and then softirq's are run before we return here to UNBLOCK.
+ * If the softirq's take too much time to run, the timer irq won't run and the 
+ * watchdog will kill us.
+ *
+ * Furthermore, if a lot of other irq's occur before we return here, the multiple_irq
+ * handler is run and it prioritizes the timer interrupt. However if we had BLOCK'ed
+ * it here, we would not get the multiple_irq at all.
+ *
+ * The non-blocking here is based on the knowledge that the timer interrupt is 
+ * registred as a fast interrupt (SA_INTERRUPT) so that we _know_ there will not
+ * be an sti() before the timer irq handler is run to acknowledge the interrupt.
+ */
+
+#define BUILD_TIMER_IRQ(nr,mask) \
+void IRQ_NAME(nr); \
+void sIRQ_NAME(nr); \
+void BAD_IRQ_NAME(nr); \
+__asm__ ( \
+          ".text\n\t" \
+          "IRQ" #nr "_interrupt:\n\t" \
+	  SAVE_ALL \
+	  "sIRQ" #nr "_interrupt:\n\t" /* shortcut for the multiple irq handler */ \
+	  "moveq "#nr",$r10\n\t" \
+	  "move.d $sp,$r11\n\t" \
+	  "jsr do_IRQ\n\t" /* irq.c, r10 and r11 are arguments */ \
+	  "moveq 0,$r9\n\t" /* make ret_from_intr realise we came from an irq */ \
+	  "jump ret_from_intr\n\t" \
+          "bad_IRQ" #nr "_interrupt:\n\t" \
+	  "push $r0\n\t" \
+	  BLOCK_IRQ(mask,nr) \
+	  "pop $r0\n\t" \
+          "reti\n\t" \
+          "nop\n");
 
 #endif  /* _ASM_IRQ_H */
 
diff -urN linux-2.4.18-pre2/include/asm-cris/pgtable.h linux/include/asm-cris/pgtable.h
--- linux-2.4.18-pre2/include/asm-cris/pgtable.h	Fri Nov  9 14:11:15 2001
+++ linux/include/asm-cris/pgtable.h	Thu Jan 10 13:28:35 2002
@@ -3,6 +3,12 @@
  * HISTORY:
  *
  * $Log: pgtable.h,v $
+ * Revision 1.14  2001/12/10 03:08:50  bjornw
+ * Added pgtable_cache_init dummy
+ *
+ * Revision 1.13  2001/11/12 18:05:38  pkj
+ * Added declaration of paging_init().
+ *
  * Revision 1.12  2001/08/11 00:28:00  bjornw
  * PAGE_CHG_MASK and PAGE_NONE had somewhat untraditional values
  *
@@ -106,6 +112,8 @@
  * the CRIS page table tree.
  */
 
+extern void paging_init(void);
+
 /* The cache doesn't need to be flushed when TLB entries change because 
  * the cache is mapped to physical memory, not virtual memory
  */
@@ -507,6 +515,6 @@
 /*
  * No page table caches to initialise
  */
-#define pgtable_cache_init()	do { } while (0)
+#define pgtable_cache_init()   do { } while (0)
 
 #endif /* _CRIS_PGTABLE_H */
diff -urN linux-2.4.18-pre2/include/asm-cris/processor.h linux/include/asm-cris/processor.h
--- linux-2.4.18-pre2/include/asm-cris/processor.h	Mon Oct  8 11:43:54 2001
+++ linux/include/asm-cris/processor.h	Thu Jan 10 13:28:35 2002
@@ -142,6 +142,6 @@
 #define init_task       (init_task_union.task)
 #define init_stack      (init_task_union.stack)
 
-#define cpu_relax()	do { } while (0)
+#define cpu_relax()     do { } while (0)
 
 #endif /* __ASM_CRIS_PROCESSOR_H */
diff -urN linux-2.4.18-pre2/include/asm-cris/unistd.h linux/include/asm-cris/unistd.h
--- linux-2.4.18-pre2/include/asm-cris/unistd.h	Fri Nov  9 14:11:15 2001
+++ linux/include/asm-cris/unistd.h	Thu Jan 10 13:28:35 2002
@@ -378,6 +378,7 @@
 static inline _syscall1(int,close,int,fd)
 static inline _syscall1(int,_exit,int,exitcode)
 static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
+static inline _syscall3(off_t,lseek,int,fd,off_t,offset,int,count)
 
   /* the following are just while developing the elinux port! */
 
diff -urN linux-2.4.18-pre2/include/asm-cris/user.h linux/include/asm-cris/user.h
--- linux-2.4.18-pre2/include/asm-cris/user.h	Thu Feb  8 16:32:44 2001
+++ linux/include/asm-cris/user.h	Thu Jan 10 13:28:35 2002
@@ -28,8 +28,51 @@
  *	to write an integer number of pages.
  */
 
+/* User mode registers, used for core dumps. In order to keep ELF_NGREG
+   sensible we let all registers be 32 bits. The csr registers are included
+   for future use. */
+struct user_regs_struct {
+        unsigned long r0;       /* General registers. */
+        unsigned long r1;
+        unsigned long r2;
+        unsigned long r3;
+        unsigned long r4;
+        unsigned long r5;
+        unsigned long r6;
+        unsigned long r7;
+        unsigned long r8;
+        unsigned long r9;
+        unsigned long r10;
+        unsigned long r11;
+        unsigned long r12;
+        unsigned long r13;
+        unsigned long sp;       /* Stack pointer. */
+        unsigned long pc;       /* Program counter. */
+        unsigned long p0;       /* Constant zero (only 8 bits). */
+        unsigned long vr;       /* Version register (only 8 bits). */
+        unsigned long p2;       /* Reserved. */
+        unsigned long p3;       /* Reserved. */
+        unsigned long p4;       /* Constant zero (only 16 bits). */
+        unsigned long ccr;      /* Condition code register (only 16 bits). */
+        unsigned long p6;       /* Reserved. */
+        unsigned long mof;      /* Multiply overflow register. */
+        unsigned long p8;       /* Constant zero. */
+        unsigned long ibr;      /* Not accessible. */
+        unsigned long irp;      /* Not accessible. */
+        unsigned long srp;      /* Subroutine return pointer. */
+        unsigned long bar;      /* Not accessible. */
+        unsigned long dccr;     /* Dword condition code register. */
+        unsigned long brp;      /* Not accessible. */
+        unsigned long usp;      /* User-mode stack pointer. Same as sp when 
+                                   in user mode. */
+        unsigned long csrinstr; /* Internal status registers. */
+        unsigned long csraddr;
+        unsigned long csrdata;
+};
+
+        
 struct user {
-	struct pt_regs	regs;			/* entire machine state */
+	struct user_regs_struct	regs;		/* entire machine state */
 	size_t		u_tsize;		/* text size (pages) */
 	size_t		u_dsize;		/* data size (pages) */
 	size_t		u_ssize;		/* stack size (pages) */
diff -urN linux-2.4.18-pre2/include/asm-i386/rwlock.h linux/include/asm-i386/rwlock.h
--- linux-2.4.18-pre2/include/asm-i386/rwlock.h	Fri Sep 22 14:07:43 2000
+++ linux/include/asm-i386/rwlock.h	Thu Jan 10 13:28:35 2002
@@ -17,6 +17,8 @@
 #ifndef _ASM_I386_RWLOCK_H
 #define _ASM_I386_RWLOCK_H
 
+#include <linux/stringify.h>
+
 #define RW_LOCK_BIAS		 0x01000000
 #define RW_LOCK_BIAS_STR	"0x01000000"
 
@@ -24,23 +26,29 @@
 	asm volatile(LOCK "subl $1,(%0)\n\t" \
 		     "js 2f\n" \
 		     "1:\n" \
-		     ".section .text.lock,\"ax\"\n" \
+		     ".subsection 1\n" \
+		     ".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n" \
+		     "_text_lock_" __stringify(KBUILD_BASENAME) ":\n" \
+		     ".endif\n" \
 		     "2:\tcall " helper "\n\t" \
 		     "jmp 1b\n" \
-		     ".previous" \
+		     ".subsection 0\n" \
 		     ::"a" (rw) : "memory")
 
 #define __build_read_lock_const(rw, helper)   \
 	asm volatile(LOCK "subl $1,%0\n\t" \
 		     "js 2f\n" \
 		     "1:\n" \
-		     ".section .text.lock,\"ax\"\n" \
+		     ".subsection 1\n" \
+		     ".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n" \
+		     "_text_lock_" __stringify(KBUILD_BASENAME) ":\n" \
+		     ".endif\n" \
 		     "2:\tpushl %%eax\n\t" \
 		     "leal %0,%%eax\n\t" \
 		     "call " helper "\n\t" \
 		     "popl %%eax\n\t" \
 		     "jmp 1b\n" \
-		     ".previous" \
+		     ".subsection 0\n" \
 		     :"=m" (*(volatile int *)rw) : : "memory")
 
 #define __build_read_lock(rw, helper)	do { \
@@ -54,23 +62,29 @@
 	asm volatile(LOCK "subl $" RW_LOCK_BIAS_STR ",(%0)\n\t" \
 		     "jnz 2f\n" \
 		     "1:\n" \
-		     ".section .text.lock,\"ax\"\n" \
+		     ".subsection 1\n" \
+		     ".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n" \
+		     "_text_lock_" __stringify(KBUILD_BASENAME) ":\n" \
+		     ".endif\n" \
 		     "2:\tcall " helper "\n\t" \
 		     "jmp 1b\n" \
-		     ".previous" \
+		     ".subsection 0\n" \
 		     ::"a" (rw) : "memory")
 
 #define __build_write_lock_const(rw, helper) \
 	asm volatile(LOCK "subl $" RW_LOCK_BIAS_STR ",(%0)\n\t" \
 		     "jnz 2f\n" \
 		     "1:\n" \
-		     ".section .text.lock,\"ax\"\n" \
+		     ".subsection 1\n" \
+		     ".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n" \
+		     "_text_lock_" __stringify(KBUILD_BASENAME) ":\n" \
+		     ".endif\n" \
 		     "2:\tpushl %%eax\n\t" \
 		     "leal %0,%%eax\n\t" \
 		     "call " helper "\n\t" \
 		     "popl %%eax\n\t" \
 		     "jmp 1b\n" \
-		     ".previous" \
+		     ".subsection 0\n" \
 		     :"=m" (*(volatile int *)rw) : : "memory")
 
 #define __build_write_lock(rw, helper)	do { \
diff -urN linux-2.4.18-pre2/include/asm-i386/rwsem.h linux/include/asm-i386/rwsem.h
--- linux-2.4.18-pre2/include/asm-i386/rwsem.h	Thu Nov 22 11:46:19 2001
+++ linux/include/asm-i386/rwsem.h	Thu Jan 10 13:28:35 2002
@@ -40,6 +40,7 @@
 
 #include <linux/list.h>
 #include <linux/spinlock.h>
+#include <linux/stringify.h>
 
 struct rwsem_waiter;
 
@@ -101,7 +102,10 @@
 LOCK_PREFIX	"  incl      (%%eax)\n\t" /* adds 0x00000001, returns the old value */
 		"  js        2f\n\t" /* jump if we weren't granted the lock */
 		"1:\n\t"
-		".section .text.lock,\"ax\"\n"
+		".subsection 1\n"
+		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
+		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
+		".endif\n"
 		"2:\n\t"
 		"  pushl     %%ecx\n\t"
 		"  pushl     %%edx\n\t"
@@ -109,7 +113,7 @@
 		"  popl      %%edx\n\t"
 		"  popl      %%ecx\n\t"
 		"  jmp       1b\n"
-		".previous"
+		".subsection 0\n"
 		"# ending down_read\n\t"
 		: "+m"(sem->count)
 		: "a"(sem)
@@ -130,13 +134,16 @@
 		"  testl     %0,%0\n\t" /* was the count 0 before? */
 		"  jnz       2f\n\t" /* jump if we weren't granted the lock */
 		"1:\n\t"
-		".section .text.lock,\"ax\"\n"
+		".subsection 1\n"
+		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
+		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
+		".endif\n"
 		"2:\n\t"
 		"  pushl     %%ecx\n\t"
 		"  call      rwsem_down_write_failed\n\t"
 		"  popl      %%ecx\n\t"
 		"  jmp       1b\n"
-		".previous\n"
+		".subsection 0\n"
 		"# ending down_write"
 		: "+d"(tmp), "+m"(sem->count)
 		: "a"(sem)
@@ -154,7 +161,10 @@
 LOCK_PREFIX	"  xadd      %%edx,(%%eax)\n\t" /* subtracts 1, returns the old value */
 		"  js        2f\n\t" /* jump if the lock is being waited upon */
 		"1:\n\t"
-		".section .text.lock,\"ax\"\n"
+		".subsection 1\n"
+		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
+		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
+		".endif\n"
 		"2:\n\t"
 		"  decw      %%dx\n\t" /* do nothing if still outstanding active readers */
 		"  jnz       1b\n\t"
@@ -162,7 +172,7 @@
 		"  call      rwsem_wake\n\t"
 		"  popl      %%ecx\n\t"
 		"  jmp       1b\n"
-		".previous\n"
+		".subsection 0\n"
 		"# ending __up_read\n"
 		: "+m"(sem->count), "+d"(tmp)
 		: "a"(sem)
@@ -180,7 +190,10 @@
 LOCK_PREFIX	"  xaddl     %%edx,(%%eax)\n\t" /* tries to transition 0xffff0001 -> 0x00000000 */
 		"  jnz       2f\n\t" /* jump if the lock is being waited upon */
 		"1:\n\t"
-		".section .text.lock,\"ax\"\n"
+		".subsection 1\n"
+		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
+		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
+		".endif\n"
 		"2:\n\t"
 		"  decw      %%dx\n\t" /* did the active count reduce to 0? */
 		"  jnz       1b\n\t" /* jump back if not */
@@ -188,7 +201,7 @@
 		"  call      rwsem_wake\n\t"
 		"  popl      %%ecx\n\t"
 		"  jmp       1b\n"
-		".previous\n"
+		".subsection 0\n"
 		"# ending __up_write\n"
 		: "+m"(sem->count)
 		: "a"(sem), "i"(-RWSEM_ACTIVE_WRITE_BIAS)
diff -urN linux-2.4.18-pre2/include/asm-i386/semaphore.h linux/include/asm-i386/semaphore.h
--- linux-2.4.18-pre2/include/asm-i386/semaphore.h	Thu Nov 22 11:46:19 2001
+++ linux/include/asm-i386/semaphore.h	Thu Jan 10 13:28:35 2002
@@ -40,6 +40,7 @@
 #include <asm/atomic.h>
 #include <linux/wait.h>
 #include <linux/rwsem.h>
+#include <linux/stringify.h>
 
 struct semaphore {
 	atomic_t count;
@@ -122,10 +123,13 @@
 		LOCK "decl %0\n\t"     /* --sem->count */
 		"js 2f\n"
 		"1:\n"
-		".section .text.lock,\"ax\"\n"
+		".subsection 1\n"
+		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
+		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
+		".endif\n"
 		"2:\tcall __down_failed\n\t"
 		"jmp 1b\n"
-		".previous"
+		".subsection 0\n"
 		:"=m" (sem->count)
 		:"c" (sem)
 		:"memory");
@@ -149,10 +153,13 @@
 		"js 2f\n\t"
 		"xorl %0,%0\n"
 		"1:\n"
-		".section .text.lock,\"ax\"\n"
+		".subsection 1\n"
+		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
+		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
+		".endif\n"
 		"2:\tcall __down_failed_interruptible\n\t"
 		"jmp 1b\n"
-		".previous"
+		".subsection 0\n"
 		:"=a" (result), "=m" (sem->count)
 		:"c" (sem)
 		:"memory");
@@ -177,10 +184,13 @@
 		"js 2f\n\t"
 		"xorl %0,%0\n"
 		"1:\n"
-		".section .text.lock,\"ax\"\n"
+		".subsection 1\n"
+		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
+		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
+		".endif\n"
 		"2:\tcall __down_failed_trylock\n\t"
 		"jmp 1b\n"
-		".previous"
+		".subsection 0\n"
 		:"=a" (result), "=m" (sem->count)
 		:"c" (sem)
 		:"memory");
@@ -203,10 +213,13 @@
 		LOCK "incl %0\n\t"     /* ++sem->count */
 		"jle 2f\n"
 		"1:\n"
-		".section .text.lock,\"ax\"\n"
+		".subsection 1\n"
+		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
+		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
+		".endif\n"
 		"2:\tcall __up_wakeup\n\t"
 		"jmp 1b\n"
-		".previous"
+		".subsection 0\n"
 		:"=m" (sem->count)
 		:"c" (sem)
 		:"memory");
diff -urN linux-2.4.18-pre2/include/asm-i386/softirq.h linux/include/asm-i386/softirq.h
--- linux-2.4.18-pre2/include/asm-i386/softirq.h	Thu Nov 22 11:46:19 2001
+++ linux/include/asm-i386/softirq.h	Thu Jan 10 13:28:35 2002
@@ -3,6 +3,7 @@
 
 #include <asm/atomic.h>
 #include <asm/hardirq.h>
+#include <linux/stringify.h>
 
 #define __cpu_bh_enable(cpu) \
 		do { barrier(); local_bh_count(cpu)--; } while (0)
@@ -33,12 +34,15 @@
 			"jnz 2f;"					\
 			"1:;"						\
 									\
-			".section .text.lock,\"ax\";"			\
+			".subsection 1;"				\
+			".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"		\
+			"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"		\
+			".endif\n"					\
 			"2: pushl %%eax; pushl %%ecx; pushl %%edx;"	\
 			"call %c1;"					\
 			"popl %%edx; popl %%ecx; popl %%eax;"		\
 			"jmp 1b;"					\
-			".previous;"					\
+			".subsection 0;"				\
 									\
 		: /* no output */					\
 		: "r" (ptr), "i" (do_softirq)				\
diff -urN linux-2.4.18-pre2/include/asm-i386/spinlock.h linux/include/asm-i386/spinlock.h
--- linux-2.4.18-pre2/include/asm-i386/spinlock.h	Thu Nov 22 11:46:19 2001
+++ linux/include/asm-i386/spinlock.h	Thu Jan 10 13:28:35 2002
@@ -5,6 +5,7 @@
 #include <asm/rwlock.h>
 #include <asm/page.h>
 #include <linux/config.h>
+#include <linux/stringify.h>
 
 extern int printk(const char * fmt, ...)
 	__attribute__ ((format (printf, 1, 2)));
@@ -56,13 +57,16 @@
 	"\n1:\t" \
 	"lock ; decb %0\n\t" \
 	"js 2f\n" \
-	".section .text.lock,\"ax\"\n" \
+	".subsection 1\n" \
+	".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n" \
+	"_text_lock_" __stringify(KBUILD_BASENAME) ":\n" \
+	".endif\n" \
 	"2:\t" \
 	"cmpb $0,%0\n\t" \
 	"rep;nop\n\t" \
 	"jle 2b\n\t" \
 	"jmp 1b\n" \
-	".previous"
+	".subsection 0\n"
 
 /*
  * This works. Despite all the confusion.
diff -urN linux-2.4.18-pre2/include/asm-m68k/semaphore.h linux/include/asm-m68k/semaphore.h
--- linux-2.4.18-pre2/include/asm-m68k/semaphore.h	Thu Oct 25 13:53:55 2001
+++ linux/include/asm-m68k/semaphore.h	Thu Jan 10 13:28:35 2002
@@ -9,6 +9,7 @@
 #include <linux/wait.h>
 #include <linux/spinlock.h>
 #include <linux/rwsem.h>
+#include <linux/stringify.h>
 
 #include <asm/system.h>
 #include <asm/atomic.h>
@@ -94,11 +95,14 @@
 		"subql #1,%0@\n\t"
 		"jmi 2f\n\t"
 		"1:\n"
-		".section .text.lock,\"ax\"\n"
+		".subsection 1\n"
 		".even\n"
+		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
+		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
+		".endif\n"
 		"2:\tpea 1b\n\t"
 		"jbra __down_failed\n"
-		".previous"
+		".subsection 0\n"
 		: /* no outputs */
 		: "a" (sem1)
 		: "memory");
@@ -119,11 +123,14 @@
 		"jmi 2f\n\t"
 		"clrl %0\n"
 		"1:\n"
-		".section .text.lock,\"ax\"\n"
+		".subsection 1\n"
 		".even\n"
+		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
+		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
+		".endif\n"
 		"2:\tpea 1b\n\t"
 		"jbra __down_failed_interruptible\n"
-		".previous"
+		".subsection 0\n"
 		: "=d" (result)
 		: "a" (sem1)
 		: "memory");
@@ -145,11 +152,14 @@
 		"jmi 2f\n\t"
 		"clrl %0\n"
 		"1:\n"
-		".section .text.lock,\"ax\"\n"
+		".subsection 1\n"
 		".even\n"
+		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
+		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
+		".endif\n"
 		"2:\tpea 1b\n\t"
 		"jbra __down_failed_trylock\n"
-		".previous"
+		".subsection 0\n"
 		: "=d" (result)
 		: "a" (sem1)
 		: "memory");
@@ -175,12 +185,15 @@
 		"addql #1,%0@\n\t"
 		"jle 2f\n"
 		"1:\n"
-		".section .text.lock,\"ax\"\n"
+		".subsection 1\n"
 		".even\n"
+		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
+		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
+		".endif\n"
 		"2:\t"
 		"pea 1b\n\t"
 		"jbra __up_wakeup\n"
-		".previous"
+		".subsection 0\n"
 		: /* no outputs */
 		: "a" (sem1)
 		: "memory");
diff -urN linux-2.4.18-pre2/include/asm-parisc/semaphore.h linux/include/asm-parisc/semaphore.h
--- linux-2.4.18-pre2/include/asm-parisc/semaphore.h	Tue Apr 17 17:19:31 2001
+++ linux/include/asm-parisc/semaphore.h	Thu Jan 10 13:28:35 2002
@@ -12,10 +12,6 @@
  *
  */
 
-/* if you're going to use out-of-line slowpaths, use .section .lock.text,
- * not .text.lock or the -ffunction-sections monster will eat you alive
- */
-
 #include <linux/spinlock.h>
 #include <linux/rwsem.h>
 
diff -urN linux-2.4.18-pre2/include/asm-parisc/spinlock.h linux/include/asm-parisc/spinlock.h
--- linux-2.4.18-pre2/include/asm-parisc/spinlock.h	Tue Dec  5 12:29:39 2000
+++ linux/include/asm-parisc/spinlock.h	Thu Jan 10 13:28:35 2002
@@ -3,10 +3,6 @@
 
 #include <asm/system.h>
 
-/* if you're going to use out-of-line slowpaths, use .section .lock.text,
- * not .text.lock or the -ffunction-sections monster will eat you alive
- */
-
 /* we seem to be the only architecture that uses 0 to mean locked - but we
  * have to.  prumpf */
 
diff -urN linux-2.4.18-pre2/include/linux/cyclades.h linux/include/linux/cyclades.h
--- linux-2.4.18-pre2/include/linux/cyclades.h	Fri May 12 11:22:31 2000
+++ linux/include/linux/cyclades.h	Thu Jan 10 13:28:35 2002
@@ -514,10 +514,13 @@
     int nports;		/* Number of ports in the card */
     int bus_index;	/* address shift - 0 for ISA, 1 for PCI */
     int	intr_enabled;	/* FW Interrupt flag - 0 disabled, 1 enabled */
+    struct resource *resource;
+    unsigned long res_start;
+    unsigned long res_len;
 #ifdef __KERNEL__
     spinlock_t card_lock;
 #else
-    uclong filler;
+    unsigned long filler;
 #endif
 };
 
diff -urN linux-2.4.18-pre2/include/linux/pagemap.h linux/include/linux/pagemap.h
--- linux-2.4.18-pre2/include/linux/pagemap.h	Thu Nov 22 11:46:44 2001
+++ linux/include/linux/pagemap.h	Thu Jan 10 13:28:35 2002
@@ -29,7 +29,7 @@
 #define PAGE_CACHE_ALIGN(addr)	(((addr)+PAGE_CACHE_SIZE-1)&PAGE_CACHE_MASK)
 
 #define page_cache_get(x)	get_page(x)
-extern void FASTCALL(page_cache_release(struct page *));
+#define page_cache_release(x)	__free_page(x)
 
 static inline struct page *page_cache_alloc(struct address_space *x)
 {
diff -urN linux-2.4.18-pre2/include/linux/stringify.h linux/include/linux/stringify.h
--- linux-2.4.18-pre2/include/linux/stringify.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/stringify.h	Thu Jan 10 13:28:35 2002
@@ -0,0 +1,12 @@
+#ifndef __LINUX_STRINGIFY_H
+#define __LINUX_STRINGIFY_H
+
+/* Indirect stringification.  Doing two levels allows the parameter to be a
+ * macro itself.  For example, compile with -DFOO=bar, __stringify(FOO)
+ * converts to "bar".
+ */
+
+#define __stringify_1(x)	#x
+#define __stringify(x)		__stringify_1(x)
+
+#endif	/* !__LINUX_STRINGIFY_H */
diff -urN linux-2.4.18-pre2/kernel/fork.c linux/kernel/fork.c
--- linux-2.4.18-pre2/kernel/fork.c	Wed Nov 21 10:18:42 2001
+++ linux/kernel/fork.c	Thu Jan 10 13:28:35 2002
@@ -219,6 +219,7 @@
 	init_rwsem(&mm->mmap_sem);
 	mm->page_table_lock = SPIN_LOCK_UNLOCKED;
 	mm->pgd = pgd_alloc(mm);
+	mm->def_flags = 0;
 	if (mm->pgd)
 		return mm;
 	free_mm(mm);
@@ -585,7 +586,14 @@
 	*p = *current;
 
 	retval = -EAGAIN;
-	if (atomic_read(&p->user->processes) >= p->rlim[RLIMIT_NPROC].rlim_cur)
+	/*
+	 * Check if we are over our maximum process limit, but be sure to
+	 * exclude root. This is needed to make it possible for login and
+	 * friends to set the per-user process limit to something lower
+	 * than the amount of processes root is running. -- Rik
+	 */
+	if (atomic_read(&p->user->processes) >= p->rlim[RLIMIT_NPROC].rlim_cur
+	              && !capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE))
 		goto bad_fork_free;
 
 	atomic_inc(&p->user->__count);
diff -urN linux-2.4.18-pre2/kernel/ksyms.c linux/kernel/ksyms.c
--- linux-2.4.18-pre2/kernel/ksyms.c	Thu Jan 10 13:28:30 2002
+++ linux/kernel/ksyms.c	Thu Jan 10 13:28:35 2002
@@ -95,7 +95,6 @@
 EXPORT_SYMBOL(alloc_pages_node);
 EXPORT_SYMBOL(__get_free_pages);
 EXPORT_SYMBOL(get_zeroed_page);
-EXPORT_SYMBOL(page_cache_release);
 EXPORT_SYMBOL(__free_pages);
 EXPORT_SYMBOL(free_pages);
 EXPORT_SYMBOL(num_physpages);
diff -urN linux-2.4.18-pre2/mm/page_alloc.c linux/mm/page_alloc.c
--- linux-2.4.18-pre2/mm/page_alloc.c	Mon Nov 19 16:35:40 2001
+++ linux/mm/page_alloc.c	Thu Jan 10 13:28:35 2002
@@ -70,6 +70,12 @@
 	struct page *base;
 	zone_t *zone;
 
+	/* Yes, think what happens when other parts of the kernel take 
+	 * a reference to a page in order to pin it for io. -ben
+	 */
+	if (PageLRU(page))
+		lru_cache_del(page);
+
 	if (page->buffers)
 		BUG();
 	if (page->mapping)
@@ -424,15 +430,6 @@
 		return (unsigned long) address;
 	}
 	return 0;
-}
-
-void page_cache_release(struct page *page)
-{
-	if (!PageReserved(page) && put_page_testzero(page)) {
-		if (PageLRU(page))
-			lru_cache_del(page);
-		__free_pages_ok(page, 0);
-	}
 }
 
 void __free_pages(struct page *page, unsigned int order)
diff -urN linux-2.4.18-pre2/mm/vmalloc.c linux/mm/vmalloc.c
--- linux-2.4.18-pre2/mm/vmalloc.c	Fri Dec 21 09:42:05 2001
+++ linux/mm/vmalloc.c	Thu Jan 10 13:28:35 2002
@@ -164,6 +164,7 @@
 		ret = 0;
 	} while (address && (address < end));
 	spin_unlock(&init_mm.page_table_lock);
+	flush_cache_all();
 	return ret;
 }
 
diff -urN linux-2.4.18-pre2/net/irda/Config.in linux/net/irda/Config.in
--- linux-2.4.18-pre2/net/irda/Config.in	Sat Nov 11 18:11:23 2000
+++ linux/net/irda/Config.in	Thu Jan 10 13:28:35 2002
@@ -14,13 +14,10 @@
       source net/irda/irnet/Config.in
       source net/irda/ircomm/Config.in
       bool '  Ultra (connectionless) protocol' CONFIG_IRDA_ULTRA
-      bool '  IrDA protocol options' CONFIG_IRDA_OPTIONS
-      if [ "$CONFIG_IRDA_OPTIONS" != "n" ]; then
-	 comment '  IrDA options'
-	 bool '    Cache last LSAP' CONFIG_IRDA_CACHE_LAST_LSAP
-	 bool '    Fast RRs' CONFIG_IRDA_FAST_RR
-	 bool '    Debug information' CONFIG_IRDA_DEBUG
-      fi
+      comment 'IrDA options'
+      bool '  Cache last LSAP' CONFIG_IRDA_CACHE_LAST_LSAP
+      bool '  Fast RRs (low latency)' CONFIG_IRDA_FAST_RR
+      bool '  Debug information' CONFIG_IRDA_DEBUG
    fi
 
    if [ "$CONFIG_IRDA" != "n" ]; then
diff -urN linux-2.4.18-pre2/net/irda/ircomm/ircomm_core.c linux/net/irda/ircomm/ircomm_core.c
--- linux-2.4.18-pre2/net/irda/ircomm/ircomm_core.c	Sun Sep 30 12:26:09 2001
+++ linux/net/irda/ircomm/ircomm_core.c	Thu Jan 10 13:28:35 2002
@@ -490,18 +490,34 @@
 { 	
 	struct ircomm_cb *self;
 	unsigned long flags;
-	int i=0;
 	
 	save_flags(flags);
 	cli();
 
 	len = 0;
 
-	len += sprintf(buf+len, "Instance %d:\n", i++);
-
 	self = (struct ircomm_cb *) hashbin_get_first(ircomm);
 	while (self != NULL) {
 		ASSERT(self->magic == IRCOMM_MAGIC, return len;);
+
+		if(self->line < 0x10)
+			len += sprintf(buf+len, "ircomm%d", self->line);
+		else
+			len += sprintf(buf+len, "irlpt%d", self->line - 0x10);
+		len += sprintf(buf+len, " state: %s, ",
+			       ircomm_state[ self->state]);
+		len += sprintf(buf+len, 
+			       "slsap_sel: %#02x, dlsap_sel: %#02x, mode:",
+			       self->slsap_sel, self->dlsap_sel); 
+		if(self->service_type & IRCOMM_3_WIRE_RAW)
+			len += sprintf(buf+len, " 3-wire-raw");
+		if(self->service_type & IRCOMM_3_WIRE)
+			len += sprintf(buf+len, " 3-wire");
+		if(self->service_type & IRCOMM_9_WIRE)
+			len += sprintf(buf+len, " 9-wire");
+		if(self->service_type & IRCOMM_CENTRONICS)
+			len += sprintf(buf+len, " Centronics");
+		len += sprintf(buf+len, "\n");
 
 		self = (struct ircomm_cb *) hashbin_get_next(ircomm);
  	} 
diff -urN linux-2.4.18-pre2/net/irda/ircomm/ircomm_lmp.c linux/net/irda/ircomm/ircomm_lmp.c
--- linux-2.4.18-pre2/net/irda/ircomm/ircomm_lmp.c	Fri Mar  2 11:12:12 2001
+++ linux/net/irda/ircomm/ircomm_lmp.c	Thu Jan 10 13:28:35 2002
@@ -103,12 +103,30 @@
  *    
  *
  */
-int ircomm_lmp_connect_response(struct ircomm_cb *self, struct sk_buff *skb)
+int ircomm_lmp_connect_response(struct ircomm_cb *self, struct sk_buff *userdata)
 {
+	struct sk_buff *skb;
 	int ret;
 
 	IRDA_DEBUG(0, __FUNCTION__"()\n");
 	
+	/* Any userdata supplied? */
+	if (userdata == NULL) {
+		skb = dev_alloc_skb(64);
+		if (!skb)
+			return -ENOMEM;
+
+		/* Reserve space for MUX and LAP header */
+		skb_reserve(skb, LMP_MAX_HEADER);
+	} else {
+		skb = userdata;
+		/*  
+		 *  Check that the client has reserved enough space for 
+		 *  headers
+		 */
+		ASSERT(skb_headroom(skb) >= LMP_MAX_HEADER, return -1;);
+	}
+
 	ret = irlmp_connect_response(self->lsap, skb);
 
 	return 0;
diff -urN linux-2.4.18-pre2/net/irda/irda_device.c linux/net/irda/irda_device.c
--- linux-2.4.18-pre2/net/irda/irda_device.c	Mon Nov 12 11:29:57 2001
+++ linux/net/irda/irda_device.c	Thu Jan 10 13:28:35 2002
@@ -81,6 +81,7 @@
 	"TV_REMOTE",
 };
 
+#ifdef CONFIG_IRDA_DEBUG
 static const char *task_state[] = {
 	"IRDA_TASK_INIT",
 	"IRDA_TASK_DONE", 
@@ -92,6 +93,7 @@
 	"IRDA_TASK_CHILD_WAIT",
 	"IRDA_TASK_CHILD_DONE",
 };
+#endif	/* CONFIG_IRDA_DEBUG */
 
 static void irda_task_timer_expired(void *data);
 
diff -urN linux-2.4.18-pre2/net/irda/iriap.c linux/net/irda/iriap.c
--- linux-2.4.18-pre2/net/irda/iriap.c	Fri Dec 21 09:42:06 2001
+++ linux/net/irda/iriap.c	Thu Jan 10 13:28:35 2002
@@ -41,6 +41,7 @@
 #include <net/irda/iriap_event.h>
 #include <net/irda/iriap.h>
 
+#ifdef CONFIG_IRDA_DEBUG
 /* FIXME: This one should go in irlmp.c */
 static const char *ias_charset_types[] = {
 	"CS_ASCII",
@@ -55,6 +56,7 @@
 	"CS_ISO_8859_9",
 	"CS_UNICODE"
 };
+#endif	/* CONFIG_IRDA_DEBUG */
 
 static hashbin_t *iriap = NULL;
 static __u32 service_handle; 
diff -urN linux-2.4.18-pre2/net/irda/irlan/irlan_common.c linux/net/irda/irlan/irlan_common.c
--- linux-2.4.18-pre2/net/irda/irlan/irlan_common.c	Sun Sep 30 12:26:09 2001
+++ linux/net/irda/irlan/irlan_common.c	Thu Jan 10 13:28:35 2002
@@ -317,8 +317,15 @@
 
 	del_timer(&self->watchdog_timer);
 
-	irlan_do_provider_event(self, IRLAN_DATA_CONNECT_INDICATION, skb);
-	irlan_do_client_event(self, IRLAN_DATA_CONNECT_INDICATION, skb);
+	/* If you want to pass the skb to *both* state machines, you will
+	 * need to skb_clone() it, so that you don't free it twice.
+	 * As the state machines don't need it, git rid of it here...
+	 * Jean II */
+	if (skb)
+		dev_kfree_skb(skb);
+
+	irlan_do_provider_event(self, IRLAN_DATA_CONNECT_INDICATION, NULL);
+	irlan_do_client_event(self, IRLAN_DATA_CONNECT_INDICATION, NULL);
 
 	if (self->provider.access_type == ACCESS_PEER) {
 		/* 
@@ -421,6 +428,13 @@
 		break;
 	}
 	
+	/* If you want to pass the skb to *both* state machines, you will
+	 * need to skb_clone() it, so that you don't free it twice.
+	 * As the state machines don't need it, git rid of it here...
+	 * Jean II */
+	if (userdata)
+		dev_kfree_skb(userdata);
+
 	irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL);
 	irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL);
 	
diff -urN linux-2.4.18-pre2/net/irda/irlan/irlan_eth.c linux/net/irda/irlan/irlan_eth.c
--- linux-2.4.18-pre2/net/irda/irlan/irlan_eth.c	Thu Feb  8 13:08:38 2001
+++ linux/net/irda/irlan/irlan_eth.c	Thu Jan 10 13:28:35 2002
@@ -61,7 +61,16 @@
 	dev->hard_start_xmit    = irlan_eth_xmit; 
 	dev->get_stats	        = irlan_eth_get_stats;
 	dev->set_multicast_list = irlan_eth_set_multicast_list;
-	dev->features          |= NETIF_F_DYNALLOC;
+
+	/* NETIF_F_DYNALLOC feature was set by irlan_eth_init() and would
+	 * cause the unregister_netdev() to do asynch completion _and_
+	 * kfree self->dev afterwards. Which is really bad because the
+	 * netdevice was not allocated separately but is embedded in
+	 * our control block and therefore gets freed with *self.
+	 * The only reason why this would have been enabled is to hide
+	 * some netdev refcount issues. If unregister_netdev() blocks
+	 * forever, tell us about it... */
+	//dev->features          |= NETIF_F_DYNALLOC;
 
 	ether_setup(dev);
 	
diff -urN linux-2.4.18-pre2/net/irda/irlap.c linux/net/irda/irlap.c
--- linux-2.4.18-pre2/net/irda/irlap.c	Fri Dec 21 09:42:06 2001
+++ linux/net/irda/irlap.c	Thu Jan 10 13:28:35 2002
@@ -59,6 +59,7 @@
 extern void irlap_queue_xmit(struct irlap_cb *self, struct sk_buff *skb);
 static void __irlap_close(struct irlap_cb *self);
 
+#ifdef CONFIG_IRDA_DEBUG
 static char *lap_reasons[] = {
 	"ERROR, NOT USED",
 	"LAP_DISC_INDICATION",
@@ -69,6 +70,7 @@
 	"LAP_PRIMARY_CONFLICT",
 	"ERROR, NOT USED",
 };
+#endif	/* CONFIG_IRDA_DEBUG */
 
 #ifdef CONFIG_PROC_FS
 int irlap_proc_read(char *, char **, off_t, int);
diff -urN linux-2.4.18-pre2/net/irda/irlap_event.c linux/net/irda/irlap_event.c
--- linux-2.4.18-pre2/net/irda/irlap_event.c	Fri Dec 21 09:42:06 2001
+++ linux/net/irda/irlap_event.c	Thu Jan 10 13:28:35 2002
@@ -77,6 +77,7 @@
 static int irlap_state_reset_check(struct irlap_cb *, IRLAP_EVENT event, 
 				   struct sk_buff *, struct irlap_info *);
 
+#ifdef CONFIG_IRDA_DEBUG
 static const char *irlap_event[] = {
 	"DISCOVERY_REQUEST",
 	"CONNECT_REQUEST",
@@ -117,6 +118,7 @@
 	"BACKOFF_TIMER_EXPIRED",
 	"MEDIA_BUSY_TIMER_EXPIRED",
 };
+#endif	/* CONFIG_IRDA_DEBUG */
 
 const char *irlap_state[] = {
 	"LAP_NDM",
@@ -312,7 +314,6 @@
 {
 	discovery_t *discovery_rsp;
 	int ret = 0;
-	int i;
 
 	ASSERT(self != NULL, return -1;);
 	ASSERT(self->magic == LAP_MAGIC, return -1;);
@@ -478,6 +479,8 @@
 		break;
 #ifdef CONFIG_IRDA_ULTRA
 	case SEND_UI_FRAME:
+	{   
+		int i;
 		/* Only allowed to repeat an operation twice */
 		for (i=0; ((i<2) && (self->media_busy == FALSE)); i++) {
 			skb = skb_dequeue(&self->txq_ultra);
@@ -492,6 +495,7 @@
 			irda_device_set_media_busy(self->netdev, TRUE);
 		}
 		break;
+	}
 	case RECV_UI_FRAME:
 		/* Only accept broadcast frames in NDM mode */
 		if (info->caddr != CBROADCAST) {
diff -urN linux-2.4.18-pre2/net/irda/irlmp_event.c linux/net/irda/irlmp_event.c
--- linux-2.4.18-pre2/net/irda/irlmp_event.c	Fri Dec 21 09:42:06 2001
+++ linux/net/irda/irlmp_event.c	Thu Jan 10 13:28:35 2002
@@ -49,6 +49,7 @@
 	"LSAP_SETUP_PEND",
 };
 
+#ifdef CONFIG_IRDA_DEBUG
 static const char *irlmp_event[] = {
 	"LM_CONNECT_REQUEST",
  	"LM_CONNECT_CONFIRM",
@@ -75,6 +76,7 @@
  	"LM_LAP_DISCOVERY_CONFIRM",
 	"LM_LAP_IDLE_TIMEOUT",
 };
+#endif	/* CONFIG_IRDA_DEBUG */
 
 /* LAP Connection control proto declarations */
 static void irlmp_state_standby  (struct lap_cb *, IRLMP_EVENT, 
diff -urN linux-2.4.18-pre2/net/irda/irsyms.c linux/net/irda/irsyms.c
--- linux-2.4.18-pre2/net/irda/irsyms.c	Fri Nov  9 14:22:17 2001
+++ linux/net/irda/irsyms.c	Thu Jan 10 13:28:35 2002
@@ -198,7 +198,7 @@
 	return 0;
 }
 
-static void __exit irda_cleanup(void)
+void __exit irda_cleanup(void)
 {
 #ifdef CONFIG_SYSCTL
 	irda_sysctl_unregister();
